ES6 introduced two new ways to declare variables const
and let
, which mostly replaces the old practice of using var
. Let's briefly look into the difference between these three ways.
var vs const vs let
var | const | let | |
Declarations | Globally scoped or function scoped. | Block scoped. | Block scoped. |
Updation and Re-declaration of variable | Updated and re-declared within its scope. | Neither be updated nor re-declared. | Updated but not re-declared. |
Initialization | Hoisted to the top of their scope and are initialized with undefined. | Hoisted to the top of their scope but are not initialized. | Hoisted to the top of their scope but are not initialized. |
Declaration | Can be declared without initialization. | Must be initialized during declaration. | Can be declared without initialization. |
Temporal Dead Zone
A variable declared by let
or const
has a temporal dead zone: When entering its scope, it can’t be accessed until execution reaches the declaration.
Variables exist in the TDZ from the place they get bound (when the variable gets bound to the scope it's inside) until it is declared (when a name is reserved in memory for that variable).
If a variable is accessed before it is declared it gives a ReferenceError
. var
does not do that it just default initialize to undefined.
Example
let tmp = true;
if (true) { // Enter new scope, TDZ starts
// Uninitialized binding for `tmp` is created
console.log(tmp); // ReferenceError
let tmp; // TDZ ends, `tmp` is initialized with `undefined`
console.log(tmp); // undefined
tmp = 1;
console.log(tmp); // 1
}
console.log(tmp); // true
Moreover, the dead zone is temporal and not spatial.
Example
if (true) { // enter new scope, TDZ starts
const func = function () {
console.log(tmp); // No Error
};
// Here we are within the TDZ and
// accessing `tmp` would cause a `ReferenceError`
let tmp= 2; // TDZ ends
func(); // called outside TDZ
}
Here, we can see even though tmp variable is used in the function but it does not give an error cause the function is called outside the TDZ.
Why Temporal Dead Zone?
You might be wondering if TDZ was not needed before ES6 why include it now? Mainly cause of hoisting.
What is hoisting?
It refers to the process where the interpreter appears to move the declaration of functions, variables, or classes to the top of the scope, prior to execution of the code. Variables are hoisted so they can be referenced before they are declared.
However, JS only hoists declarations, not initialization. Variables are hoisted and assigned a default value. Due to which a variable declared with var
has a default value as undefined
but a variable declared as let
or const
does not have a default value resulting in an exception when read before initialization.
console.log(tmp); // Throws ReferenceError exceptions as tmp variable value is not initialized
let tmp = 1; // Initialization
Thus, due to this reason we have TDZ in
const
and let
but not var
.
How to avoid TDZ Errors?
We just need to make sure we define the const
and let
variables at the top of the scope.