Temporal Dead Zone in JavaScript

Published: November 2nd, 2020

While digging deeper into the concepts of ES6, I stumbled across the term "Temporal Dead Zone". I was curious and found out that I was unknowing of the term itself, but knew the concept of it. I didn't run into the issues too often, but it is absolutely helpful to know why it occurs and how to avoid it. So follow me on my journey through the Temporal Dead Zone.

To get it right off the bat:

The Temporal Dead Zone describes the state, when variables are unreachable.

Anonymous

The term isn't mentioned in the EcmaScript Language Specification but is used from us programmers to give the child a name.

So what does that mean? And why haven't I encountered this when writing code in the past (before ES6).

Variable declaration and hoisting

Let's start with an extremely simplified example:

console.log(myVar); 
var myVar = 'Hello World'
console.log(myVar);

What happens:

The JavaScript Engine parses the code and hoists myVar to the top of its scope. Since we declared it with var it and creates a global property. We then try to log myVar before we declare it which logs us undefined, the default initialization value for var. Then we initialize its correct value and log it again. This time we log Hello World. So far so good. Our code doesn't break and we can just live with the undefined state.

Let's step it up and write this in ES6 (so let's use let instead of var):

console.log(myVar);
let myVar = 'Hello World'
console.log(myVar);

What happens:

Again the JavaScript Engine parses the code and hoists myVar. This time we declared it with let which doesn't create a global property, since let (also const) are block scoped. Both are hoisted into the Temporal Dead Zone. We then try again to log myVar but Ooops! This throws an Uncaught ReferenceError: can't access lexical declaration 'myVar' before initialization and breaks our code!

Hoisting table

On ExploringJS, you can find a really nifty table, showing all the variants how variables can be declared and how they are hoisted.

Type Hoisting Scope Global Properties
var Declaration Function Yes
let Temporal Dead Zone Block No
const Temporal Dead Zone Block No
function Complete Block Yes
class No Block No
import Complete Module Global No

I found contradictory statements about the hoisting of let and const. Some say they are not hoisted at all, some say the are but into the TDZ. I follow the later one, because a short example shows in my opinion that they are indeed hoisted; to the Temporal Dead Zone.

// TDZ
// TDZ
// TDZ
console.log(myHoistedLet); // Uncaught ReferenceError: can't access lexical declaration 'myHoistedLet' before initialization
// TDZ
// TDZ
// TDZ
let myHoistedLet = 'Awesome!';

Temporal Dead Zone explained

As quoted above, the state between the declaration of a variable with let or const and its initialization is called the Temporal Dead Zone. This is because of its lifecycle in the JavaScript Engine. So let's have a closer look on this:

Lifecycle of let and const

Both variables are bound to their blockscope. So the cycle looks a bit like this:

// TDZ
console.log(myVar); // ReferenceError
// TDZ
let myVar; // TDZ ends, myVar is initialized without a value
console.log(myVar); // undefined because of the default behaviour

myVar = 'Awesome!'; // initializer is called with a value
console.log(myVar); // Awesome!

Why is there a Temporal Dead Zone?

Plain and simple: For debugging and code stability.

The block scoping of let and const helps with globals pollution plus the addition of a thrown ReferenceError is gold. If you are not only programming in JavaScript but for example in Java, the poor handling of (undefined) variables are just a pain.

I am a big fan of failing fast and early, so postponing errors from declaration to the usage is not a good practice. And it also is time consuming. Ever had a bug where the result just is not be correct? Debugging hundreds of lines of code just to find a false declaration of a var? Been there, debugged that. No bueno! Failing already at the declaration of the variable would have prevented that error.

Everything to get a more stable codebase is great. And with knowing how JavaScript handles the Temporal Dead Zone, this should be a piece of cake.