Temporal Dead Zone explained in simple terms


As ES2015 (ES6) gains popularity, you will probably hear about Temporal Dead Zone(TDZ) a lot more, as you hear about “hoisting” today. In this post I have tried to explain TDZ in simple terms with examples. The only prerequisite for this blog is knowledge of the let keyword. “Let” keyword was introduced in ES6 and is meant to provide block-level scoping to variables as compared to it’s ancestor, “var” which provides function-level scoping. If you don’t know about let yet, go ahead and read it from MDN here.


So let’s start off with a simple example:

(function() { 
var b = 10;
console.log(b);
}());

Running the above in any browser will print a 10 on the console. Now let’s modify the code a bit — let’s move the declaration of the variable b below it’s usage.

(function() { 
console.log(b);
var b = 10;
}());

The above will print undefined on any browser, the reason being hoisting. Any variables declared in a function scope are pushed to the top of the function by the JavaScript compiler. Hence, while running the console statement the JavaScript compiler knows that a variable named b exists and that it is defined somewhere down in the function, but it’s current value is undefined.

Now, let’s add some more spice to our example. Consider the output of the below program:

var b = 20;
(function() {
console.log(b);
var b = 10;
}());

If you guessed the output will be 20, you need to grow a little more gray hairs with JavaScript. The console will still read undefined. The reason is exactly the same as above — the JavaScript compiler will hoist the variable, b in the IIFE, causing it to shadow the outer b(initialized with a value of 20).

However, if we have the following code,

var b = 20;
(function() {
console.log(b);
}());

then the output is definitely 20.

We have not talked anything related to ES6 yet. So let’s jump into the mystic let keyword and discover how let would differ from the var variable in the above examples.

(function() { 
let b = 10;
console.log(b);
}());

First things first. All the examples involving let has been run on my Firefox browser(v42). Let still doesn’t have a good support across all browsers. Coming back to our example, if you have read about or tried let, you would have guessed that the console would print 10. No surprises. Let’s move to a much more interesting case:

(function() { 
console.log(b);
let b = 10;
}());

The above code will annoy the JavaScript compiler and make it spit a ReferenceError: can’t access lexical declaration `b’ before initialization.

To explain the error in simple terms, the compiler is complaining that we can’t use our variable, b, until we initialize it. The point to note here is that the above statement is true at a scope level — modules, block, classes, or a function scope, i.e. if you initialize a variable in a scope using let, make sure you don’t use it before the initialization statement. The zone between the start of the scope until the initialization statement of the variable is a temporal dead zone for the variable where, even though the compiler knows that the variable exists, it won’t let you use them.

You would have guessed it correctly that the below program would output the same ReferenceError:

let b = 20;
(function() {
console.log(b);
let b = 10;
}());

since the IIFE would create a new scope and a TDZ for b until b is initialized. One subtle point to note is that let initialized the variables with the undefined value by default(same as var). So the below program:

let x;
x;

will output undefined.

Hopefully, the above examples would have cleared the air a bit over the meaning of the cryptic term, TDZ. TDZ will become a popular subject to discuss about once all the browsers start supporting it completely.