Understanding Temporal Dead Zone in JavaScript

Sunday, July 26, 2020

Temporal Dead Zone is an advanced and interesting topic in JavaScript. To understand what it is and how things work, we need to understand different ways of variable declaration in JavaScript.

Var, Let, and Const

ES6 brought a lot of new features to the language and one of them was the ability to define variables using different keywords which have subtle differences among them. ECMA Reference

We can declare variables in three ways:

var name = "Yomesh";
let surname = "Gupta";
const profession = "Software Engineer";

Let us understand what are the differences between these three.

Scoping

In the most simple terms, scoping is the area in which a variable is accessible. Scoping of a variable is controlled by the location of its declaration. Let us consider an example.

function init() {
    var firstname = "Yomesh";
    console.log(firstname); // Yomesh
}
console.log(firstname); // Error

In the above code snippet, variable firstname is declared inside the function init, i.e. anywhere inside this function we can get or set the value of the variable. However, accessing the same variable outside the function would lead to:

ReferenceError: firstname is not defined

Types of Scoping

We have three types of scoping, namely, Global, Function, and Block scoping. Now you must be thinking what does this all exactly mean? Think no more, it is example time!

Global Scope

var firstname = "Yomesh";

function sayName() {
    console.log(firstname); // firstname is accessible here
}

function nestedSayName() {
    function sayNameNow() {
        console.log(firstname); // firstname is accessible here
    }
    sayNameNow();
}

sayName(); // console logs "Yomesh"
nestedSayName(); // console logs "Yomesh"

In the above code snippet, the variable firstname is declared outside the functions. Any variable which is declared outside of a function will fall into global scope and is accessible to all parts of the code.

Function Scope

function sayName() {
    var firstname = "Yomesh";
    console.log(firstname); // firstname is accessible here
    function sayNameNow() {
        // firstname is accessible here and we can update it.
        firstname = "Ronaldo";
        console.log(firstname); // Ronaldo
    }
    sayNameNow();
}
sayName();

In the above code snippet, variable firstname is declared inside the function sayName. It is scoped to that function i.e. it can be used inside the sayName function or any nested functions. All variables declared via var are function scoped.

Block Scope

function sayName() {
    var condition = true;
    if (condition) {
        let firstname = "Cristiano";
        const surname = "Ronaldo";
        console.log(firstname, surname); // Cristiano Ronaldo
    }
    console.log(firstname, surname); // ReferenceError
}

In the above code snippet, variables firstname and surname are declared inside the scope created by the if code block. The first console.log statement logs the value correctly as variables are accessible in that scope. However, the second console.log throws an error because variables simply don't exist there.

Declaration & Initialisation

A variable declaration introduces a new identifier. JavaScript interpreter creates a new variable during the creation phase.

var name = "Yomesh"; // allowed
var name = "Cristiano"; // allowed
let surname = "Gupta"; // allowed
let surname = "Ronaldo"; // Error
const profession = "Software Engineer"; // allowed
const profession = "Footballer"; // Error

Any variable declared via var can be re-declared as many times as we want. However, let and const can be only declared once in their respective scope. Variables declared via var have the default value of undefined. Variables declared via let and const exist but without a value and cannot be accessed before they are assigned (more on this later).

Binding to the Global Object

var firstname = "Yomesh";
let surname = "Gupta";
const profession = "Software Engineer";

// Yomesh Gupta Software Engineer
console.log(firstname, surname, profession);

// Yomesh undefined undefined
console.log(window.firstname, window.surname, window.profession);

Variables declared via var keyword in the global scope are automatically attached to the global object i.e. window in the browsers. The same is not the case with let and const.

Understanding Temporal Dead Zone

JavaScript engine runs through our code in two phases — the Creation phase and the Execution phase. In the first phase, the engine goes through the code and allocates memory for the variables.

console.log(firstname); // undefined
var firstname = "Yomesh";

In the same phase, the variables declared via var are assigned the value undefined and that is why in the above code snippet you will get the value of the variable firstname as undefined. This is also what we know as Hoisting.

console.log(firstname); // ReferenceError
let firstname = "Yomesh";

In the case of the variables declared via let and const, they are also allocated memory but they are not assigned the value undefined initially, i.e. variable declarations do hoist but they throw an error till initialized. Let us consider an example.

/*
  Since variables are hoisted
  it would be equivalent to
  
  let firstname;
  Beginning of the temporal dead zone
*/

console.log(firstname); // ReferenceError as accessed in the TDZ

function init() {
  ...
}

function processing() {
  ...
}

var surname = "Gupta";
var profession = "Software Engineer";

let firstname = "Yomesh"; // Ending of the temporal dead zone
console.log(firstname); // Yomesh

As you can see, there exists a region (temporal dead zone) for the variable firstname which begins from the place where the variable is hoisted (its scope is created) till the place where the variable is initialized. Temporal Dead Zone is a good way to indicate bugs in the code. Accessing variables before declaration is often a root cause of errors.

In the Execution phase, variables are assigned their actual values and it is perfectly fine to use them afterwards.

// Accessing `name` here before execution phase assigns the value to the variable would throw a ReferenceError due to TDZ.
// console.log(name);
const name = "Yomesh";
console.log(name); // Yomesh | Perfectly fine to use here

Temporal Dead Zone is everywhere. We considered the examples related to variable declarations but it also exists in newer features like Default Parameters.

We hope this article helped you in some way and gave you more clarity. Let me know your views in the comments or by clicking here.