172

I've heard that accessing let and const values before they are initialized can cause a ReferenceError because of something called the temporal dead zone.

What is the temporal dead zone, how does it relate to scope and hoisting, and in what situations is it encountered?

Mark Amery
  • 110,735
  • 57
  • 354
  • 402
joews
  • 27,174
  • 10
  • 70
  • 84
  • 6
    possible duplicate of [Are variables declared with let or const not hoisted in ES6?](http://stackoverflow.com/q/31219420/1048572) - although the question doesn't focus on the TDZ, the answers are basically the same – Bergi Oct 18 '15 at 21:06

3 Answers3

229

let and const have two broad differences from var:

  1. They are block scoped.
  2. Accessing a var before it is declared has the result undefined; accessing a let or const before it is declared throws ReferenceError:

console.log(aVar); // undefined
console.log(aLet); // causes ReferenceError: aLet is not defined
var aVar = 1;
let aLet = 2;

It appears from these examples that let declarations (and const, which works the same way) may not be hoisted, since aLet does not appear to exist before it is assigned a value.

That is not the case, however—let and const are hoisted (like var, class and function), but there is a period between entering scope and being declared where they cannot be accessed. This period is the temporal dead zone (TDZ).

The TDZ ends when aLet is declared, rather than assigned:

//console.log(aLet)  // would throw ReferenceError

let aLet;
console.log(aLet); // undefined
aLet = 10;
console.log(aLet); // 10

This example shows that let is hoisted:

let x = 'outer value';
(function() {
  // start TDZ for x
  console.log(x);
  let x = 'inner value'; // declaration ends TDZ for x
}());

Credit: Temporal Dead Zone (TDZ) demystified

Accessing x in the inner scope still causes a ReferenceError. If let were not hoisted, it would log outer value.

The TDZ is a good thing because it helps to highlight bugs—accessing a value before it has been declared is rarely intentional.

The TDZ also applies to default function arguments. Arguments are evaluated left to right, and each argument is in the TDZ until it is assigned:

// b is in TDZ until its value is assigned
function testDefaults(a=b, b) { }
testDefaults(undefined, 1); // throws ReferenceError because the evaluation of a reads b before it has been evaluated.

The TDZ is not enabled by default in the babel.js transpiler. Turn on "high compliance" mode to use it in the REPL. Supply the es6.spec.blockScoping flag to use it with the CLI or as a library.

Recommended further reading: TDZ demystified and ES6 Let, Const and the “Temporal Dead Zone” (TDZ) in Depth.

vigzmv
  • 1
  • 2
  • 4
joews
  • 27,174
  • 10
  • 70
  • 84
  • 3
    Also interesting: [Why is there a temporal dead zone](http://www.2ality.com/2015/10/why-tdz.html) – a better oliver Oct 19 '15 at 13:53
  • @zeroflagL good link, thanks. Also it says: "foo is not undeclared, it is uninitialized", that language would be helpful to clarify/correct in the answer above. `let foo` in a block causes it to be hoisted and declared at the top of that block. The line of `let foo` causes it to be initialised. And `foo = xyz` causes it to be assigned a value. – AJP Jun 04 '17 at 01:04
  • 2
    I think this is a great post! However, I was under the impression that 'let' was not subject to hoisting? I found this in the Mozilla docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_Dead_Zone_and_errors_with_let I'm not trying to be a curmudgeon, I was just curious and am opened to clarification. – dmarges Oct 03 '17 at 21:39
  • @dmarges the third code snippet shows that `let` is hoisted - otherwise the snippet would log `"outer value"`. Instead it throws a `ReferenceError` because `x` is the hoisted inner variable, which is in the TDZ. – joews Oct 04 '17 at 08:22
  • It shows the opposite though. If let vars are hoisted, then the output would be "undefined". let vars are block scope, so the JS compiler would only really care about the let x in the IIFE since that would be a distinct block. Even then, it is throwing the ReferenceError because x variable is not in the variable table as far as the compiler is concerned. Replacing the let with var in the IIFE proves this because the output would be undefined. – dmarges Oct 05 '17 at 00:27
  • Nope - both are hoisted (the `var`/`let`, but not any initial assignment are logically moved to the top of their function [or block for let - but it's a function here]), but before assignment `var`s are undefined and `let`s throw a `ReferenceError` because of the TDZ. – joews Oct 05 '17 at 08:31
  • 2
    @jeows The MDN page still says they're not hoisted. You should try to edit that, if you are truly certain of what you are saying. I think I should post a question about that. – doubleOrt Dec 05 '17 at 22:01
  • If it weren't for the behavior in the third example, everyone could say that they are not hoisted, and we would have one fewer intricate detail. – doubleOrt Dec 05 '17 at 22:03
  • Without hoisting the third example would have the variable x referencing two different values in the same scope, which would be another intricate and very confusing detail. – joews Dec 05 '17 at 23:09
  • 1
    @joews IMO, you could either say they are hoisted but they can't be accessed before their declaration is reached because of the TDZ, or you could say they are not hoisted but the TDZ will cause any references to them to throw an error. Practically, both statements are equally true. Except, I think, you are using the term "hoisting" in an abstract sense, as in "hoisting = whenever the engine is aware of that variable's existence". Is that why ? Plus, what do the specs say on that ? – doubleOrt Feb 15 '18 at 15:15
12

Hoisting:
let,const,var are all get hoisted process.
(whats mean they go upper and declare in the top of the scope.)

Initialisation:

  • var go also through the initial process, and get initial value of undefined.
  • while let,const didn't go throw the initial process, so their values are still inaccessible, although they already declared. whats put them in temporal dead zone

So in shortly:

hoisting process: var, let, const
Initialisation process: var

ofir_aghai
  • 2,223
  • 1
  • 28
  • 33
6

In case of let and const variables, Basically, Temporal Dead Zone is a zone

"before your variable is declared",

i.e where you can not access the value of these variables, it will throw an error.

ex.

let sum = a + 5;        //---------
//some other code       //         | ------>  this is TDZ for variable a
                        //         |
console.log(sum)        //---------
let a = 5;

above code gives an error

the same code will not give an error when we use var for variable 'a',

ex.

var sum = a;                            
console.log(sum)     //prints undefined
var a = 5;
niranjan harpale
  • 1,387
  • 1
  • 13
  • 19