Temporal Dead Zone and Hoisting are two essential terms in JavaScript. But understanding how they work can easily confuse you if you don't approach them properly.

But don't fret! This article is here to help you get a good grasp of the two terms.

So relax, grab your favorite cup of coffee, and let’s get started with TDZ.

What Exactly Is a Temporal Dead Zone in JavaScript?

A temporal dead zone (TDZ) is the area of a block where a variable is inaccessible until the moment the computer completely initializes it with a value.

  • A block is a pair of braces ({...}) used to group multiple statements.
  • Initialization occurs when you assign an initial value to a variable.

Suppose you attempt to access a variable before its complete initialization. In such a case, JavaScript will throw a ReferenceError.

So, to prevent JavaScript from throwing such an error, you’ve got to remember to access your variables from outside the temporal dead zone.

But where exactly does the TDZ begin and end? Let’s find out below.

Where Exactly Is the Scope of a Temporal Dead Zone?

A block’s temporal dead zone starts at the beginning of the block’s local scope. It ends when the computer fully initializes your variable with a value.

Here’s an example:

{
  // bestFood’s TDZ starts here (at the beginning of this block’s local scope)
  // bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  console.log(bestFood); // returns ReferenceError because bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  let bestFood = "Vegetable Fried Rice"; // bestFood’s TDZ ends here
  // bestFood’s TDZ does not exist here
  // bestFood’s TDZ does not exist here
  // bestFood’s TDZ does not exist here
}

Try it on StackBlitz

In the snippet above, the block’s TDZ starts from the opening curly bracket ({) and ends once the computer initializes bestFood with the string value "Vegetable Fried Rice".

When you run the snippet, you will see that the console.log() statement will return a ReferenceError.

JavaScript will return a ReferenceError because we used the console.log() code to access bestFood before its complete initialization. In other words, we invoked bestFood within the temporal dead zone.

However, here is how you can access bestFood successfully after its complete initialization:

{
  // TDZ starts here (at the beginning of this block’s local scope)
  // bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  let bestFood = "Vegetable Fried Rice"; // bestFood’s TDZ ends here
  console.log(bestFood); // returns "Vegetable Fried Rice" because bestFood’s TDZ does not exist here
  // bestFood’s TDZ does not exist here
  // bestFood’s TDZ does not exist here
}

Try it on StackBlitz

Now, consider this example:

{
  // TDZ starts here (at the beginning of this block’s local scope)
  // bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  // bestFood’s TDZ continues here
  let bestFood; // bestFood’s TDZ ends here
  console.log(bestFood); // returns undefined because bestFood’s TDZ does not exist here
  bestFood = "Vegetable Fried Rice"; // bestFood’s TDZ does not exist here
  console.log(bestFood); // returns "Vegetable Fried Rice" because bestFood’s TDZ does not exist here
}

Try it on StackBlitz

You can see that the first console.log code in the snippet above returned undefined.

JavaScript returned undefined because we did not assign bestFood a value before using (invoking) it. As such, JavaScript defaulted its value to undefined.

Keep in mind that you must specify a value for a const variable while declaring it. Apart from this exception, all other temporal dead zone principles of let variables apply also to const. However, var works differently.

How Does Var’s TDZ Differ from Let and Const Variables?

The main difference between the temporal dead zone of a var, let, and const variable is when their TDZ ends.

For instance, consider this code:

{
  // bestFood’s TDZ starts and ends here
  console.log(bestFood); // returns undefined because bestFood’s TDZ does not exist here
  var bestFood = "Vegetable Fried Rice"; // bestFood’s TDZ does not exist here
  console.log(bestFood); // returns "Vegetable Fried Rice" because bestFood’s TDZ does not exist here
  // bestFood’s TDZ does not exist here
  // bestFood’s TDZ does not exist here
}

Try it on StackBlitz

When you run the snippet above, you will see that the first console.log statement will return undefined.

The console.log statement successfully returned a value (undefined) because JavaScript automatically assigns undefined to a hoisted var variable.

In other words, when the computer hoists a var variable, it automatically initializes the variable with the value undefined.

In contrast, JavaScript does not initialize a let (or const) variable with any value whenever it hoists the variable. Instead, the variable remains dead and inaccessible.

Therefore, a let (or const) variable’s TDZ ends when JavaScript fully initializes it with the value specified during its declaration.

However, a var variable’s TDZ ends immediately after its hoisting—not when the variable gets fully initialized with the value specified during its declaration.

But what exactly does “hoisting” mean? Let’s find out below.

What Exactly Does Hoisting Mean in JavaScript?

Hoisting refers to JavaScript giving higher precedence to the declaration of variables, classes, and functions during a program’s execution.

Hoisting makes the computer process declarations before any other code.

Note: Hoisting does not mean JavaScript rearranges or moves code above one another.

Hoisting simply gives higher specificity to JavaScript declarations. Thus, it makes the computer read and process declarations first before analyzing any other code in a program.

For instance, consider this snippet:

{
  // Declare a variable:
  let bestFood = "Fish and Chips";

  // Declare another variable:
  let myBestMeal = function () {
    console.log(bestFood);
    let bestFood = "Vegetable Fried Rice";
  };

  // Invoke myBestMeal function:
  myBestMeal();
}

// The code above will return:
"Uncaught ReferenceError: Cannot access 'bestFood' before initialization"

Try it on StackBlitz

The snippet above returned a ReferenceError because of the order of precedence by which the computer executed each code.

In other words, the program’s declarations got higher precedence over initializations, invocations, and other code.

Let’s go through a step-by-step tour of how JavaScript executed the snippet above.

How JavaScript Hoisting Works Step-by-Step

Below is a walk-through of how JavaScript executed the previous snippet.

1. JavaScript parsed the first bestFood declaration

let bestFood // This is the first bestFood declaration in the program

The first bestFood variable declaration is the first code the computer analyzed.

Note that after the computer read the bestFood variable declaration, JavaScript automatically kept the variable in a temporal dead zone until it got fully initialized.

Therefore, any attempt to access bestFood before its complete initialization would return a ReferenceError.

2. The computer parsed myBestMeal variable declaration

let myBestMeal

The myBestMeal variable declaration was the second code JavaScript analyzed.

Immediately after the computer read the myBestMeal variable declaration, JavaScript automatically kept the variable in a temporal dead zone until it got fully initialized.

Therefore, any attempt to access myBestMeal before its complete initialization would return a ReferenceError.

3. The computer initialized the bestFood variable

bestFood = "Fish and Chips";

The computer’s third step was to initialize bestFood with the “Fish and Chips” string value.

Therefore, invoking bestFood at this point would return “Fish and Chips”.

4. JavaScript initialized myBestMeal variable

myBestMeal = function() {
  console.log(bestFood);
  let bestFood = "Vegetable Fried Rice";
};

Fourthly, JavaScript initialized myBestMeal with the specified function. So, if you had invoked myBestMeal at this point, the function would have returned.

5. The computer invoked myBestMeal’s function

myBestMeal();

The invocation of myBestMeal’s function was the computer’s fifth action.

After the invocation, the computer processed each code in the function’s block. However, the declarations had higher precedence over other code.

6. JavaScript parsed the function’s bestFood declaration

let bestFood // This is the second bestFood declaration in the program

JavaScript’s sixth task was to analyze the function’s bestFood variable declaration.

After the analysis, JavaScript automatically kept the variable in a temporal dead zone—until its complete initialization.

Therefore, any attempt to access bestFood before its complete initialization would return a ReferenceError.

7. The computer parsed the function’s console.log statement

console.log(bestFood);

Finally, the computer read the console.log statement—which instructed the system to log bestFood’s content to the browser’s console.

However, remember that the computer has not fully initialized the function’s bestFood variable yet. As such, the variable is currently in a temporal dead zone.

Therefore, the system’s attempt to access the variable returned a ReferenceError.

Note: After the ReferenceError returned, the computer stopped reading the function’s code. Therefore, JavaScript did not initialize the function’s bestFood variable with "Vegetable Fried Rice".

Wrapping It Up

Let’s see the previous walk-through of our program in one piece:

let bestFood // 1. JavaScript parsed the first bestFood declaration

let myBestMeal // 2. the computer parsed myBestMeal variable declaration

bestFood = "Fish and Chips"; // 3. the computer initialized the bestFood variable

myBestMeal = function () {
  console.log(bestFood);
  let bestFood = "Vegetable Fried Rice";
}; // 4. JavaScript initialized myBestMeal variable

myBestMeal(); // 5. the computer invoked myBestMeal’s function

let bestFood // 6. JavaScript parsed the function’s bestFood declaration

console.log(bestFood); // 7. the computer parsed the function’s console.log statement

Uncaught ReferenceError // bestFood’s invocation returned an Error

You can see that JavaScript processed the program’s declarations before other code.

The parsing of declarations before other code in a program is what we call “hoisting”.

Overview

This article discussed what temporal dead zone and hoisting mean in JavaScript. We also used examples to illustrate how they both work.

Thanks for reading!

And here's a useful ReactJS resource:

I wrote a book about React!

  • It's beginners friendly ✔
  • It has live code snippets ✔
  • It contains scalable projects ✔
  • It has plenty of easy-to-grasp examples ✔

The React Explained Clearly book is all you need to understand ReactJS.

React Explained Clearly Book Now Available at Amazon