Temporal Dead Zone, Scope and Hoisting

ES6+ Syntax

With ES6, two very important ways to declare a variable were introduced, let and const. These were introduced to replace var which was previous standard way of declaring a variable.

Although by definition let and var are functionally similar. They are interpreted differently in some ways.

Temporal Dead Zone

The area of code before the variable is initialized by let or const is called Temporal Dead Zone (TDZ)

// Temporal Dead Zone
// Temporal Dead Zone
let a = 1;
console.log(a);

This concept is introduced with let and const and is not applicable for var

If we try to access a variable declared using let or const before it is initialized returns a Reference Error. This is because of TDZ.

console.log(a); // returns "Reference Error" (a is in TDZ)
let a = 1;

Where as for var, the console returns undefined

console.log(a); // returns "undefined"
var a = 1;

Scope

There are three types of scopes in JavaScript. Global Scope, Function Scope and Block Scope. Block Scope is relatively new, and is introduced with ES6, let and const

Global Scope is the scope of the file/program. Once declared in Global Scope, the variable can be accessed anywhere in the file/program. (Ofcourse, exception is TDZ in the case of let and const)

let num = 1;
{
    let n = 2;
    num = n + num;
    console.log(num); // logs 3
}
console.log(num); // logs 3

But if num is in TDZ...

let num = 1;
{
    let n = 2;
    let num = n + num; // throws "Reference Error", because num is not initialized in this Block Scope
    console.log(num); 
}
console.log(num);

Block Scope is the code between { } Let's try the above code with var

var num = 1;
{
    var n = 2;
    var num = n + num;
    console.log(num); // logs 3
}
console.log(num); // logs 3

Function Scope is the scope inside a function.

let num = 1;
function addNum(n) {
    num = n + num;
    return num;
}
console.log(addNum(2)); // logs 3
console.log(num); // logs 3

The above code works because num is the declared in the Block of file/program.

let num = 1;
function addNum(n) {
    let num = n + num; // throws "Reference Error", because num is not yet initialized in the block of function
    return num;
}
console.log(addNum(2)); 
console.log(num);

Let's try the same examples with var

var num = 1;
function addNum(n) {
    num = n + num; 
    return num;
}
console.log(addNum(2)); // logs 3
console.log(num); // logs 3
var num = 1;
function addNum(n) {
    var num = n + num; 
    return num;
}
console.log(addNum(2)); // logs NaN
console.log(num); // logs 1

The num inside the function is being refernced before it's declaration, but since var doesn't have TDZ, it is initialized as undefined when it is hoisted. So when the function is called undefined is added to 2 and returns NaN

If you notice, When num is declared with var inside a function, the num outside the function is not used. But when num is declared with var inside a block, the num outside the block is used. This is because var is Functional Scoped, not Block Scoped.

Whereas when num declared with let, it throwed "Reference Error" when num is accessed before initialized, because of TDZ and because let is Block Scoped

Hoisting

All the variables and functions in a JavaScript program are hoisted, i.e., a memory location is allocated to the variables and functions according to thier scopes. Although let and const are hoiseted, a value is not assigned until a value is assigned to the variable creating TDZ in between.