Table of content
Introduction
Have you ever wondered why we get ReferenceError
while accessing the variable declared with let
or const
before their declaration or initialization? That is because the variable entered into the Temporal Dead Zone.
It is very important to understand the working principle of Hoisting and Temporal Dead Zone so that you can write error-free and bug-free code.
Temporal Dead Zone is a time period where a variable declared with let
and const
exists but cannot be accessible to use it or to assign it a value.
TDZ of a variable starts at the beginning of the scope in which the variable is declared and ends at the point of the declaration of a variable.
Accessing the variable in this TDZ throws a ReferenceError
.
For example
//TDZ of foo starts here
.
.
.
//TDZ of foo continues here
.
.
let foo="bar" //TDZ of foo ends here
.
.
.
// TDZ of foo doesn't exist here
How hoisting works in JavaScript
Temporal Dead Zone and Hoisting are two related concepts. Before hoisting let's first discuss how JavaScript
engine executes a program in short. In JavaScript
the programβs declarations got higher precedence over initializations, invocations, and other code. Hence whenever we declare a variable, JS engine reads it first and this is what we call Hoisting in JavaScript
.
Note: Hoisting does not mean JavaScript
rearranges or moves code above one another.
You can read more about how hoisting works for var, let, const, function declaration and function expressions here.
When we declare the variables, JS engine hoist
the variables and keeps them inside the TDZ (Temporal Dead Zone) until execution reaches the line of code where the actual declaration is made.
What is a Temporal Dead Zone (TDZ)
The Temporal Dead Zone is a specific phase during the execution of your code where a variable declared with let
or const
exists but cannot be accessed.
This occurs from the start of the block or scope in which the variable is declared until the actual declaration is reached in the code. If you try to access the variable within the Temporal Dead Zone, a ReferenceError
will be thrown.
Keeping variables in TDZ works differently for var, let, const, function declaration and function expressions. Let's see all of these below.
TDZ of let variable
TDZ of a variable declared with let
starts at the beginning of the scope in which it is being declared and ends at the point/line of code where it is being declared.
For example
//TDZ of foo starts here
.
.
.
//TDZ of foo continues here
.
.
let foo="bar" //TDZ of foo ends here
.
.
// TDZ of foo doesn't exist here
.
.
Here, when JS engine starts its execution it first sees the declared foo
variable and keeps it in the Temporal Dead Zone until it reaches the place in the code where the variable is declared or initialized. And if we try to access the variable before it gets initialized will return a ReferenceError
.
When the execution reaches the point where we have done the actual declaration of foo
variable TDZ
of foo
ends and if we do console.log
it will either return the initialized value or undefined
(if not initialized with a value)
For example
//TDZ of foo starts here
//TDZ of foo continues here
console.log(foo) //ReferenceError: foo is not defined
//TDZ of foo continues here
let foo="bar" //TDZ of foo ends here
console.log(foo) //bar
// TDZ of foo doesn't exist here
If we do console.log(foo)
before the initialization of foo
variable it will give ReferenceError
as the variable lies in TDZ.
But the second console.log(foo)
returns bar
because the TDZ of foo
variable ends when we have declared the variable.
lets see the second example,
//TDZ of foo starts here
//TDZ of foo continues here
console.log(foo) //ReferenceError: foo is not defined
//TDZ of foo continues here
let foo //TDZ of foo ends here
console.log(foo) //undefined
// TDZ of foo doesn't exist here
The second console.log(foo)
returns undefined
.
Explanation: While inside the TDZ, the variable has not been initialized with a value The variable foo
is initialized with a value when execution reaches the place in the code where it is declared. If no initial value was specified with the variable declaration, it will be initialized with a value of undefined
.
TDZ of const variable
TDZ of const
is similar to let
except for one difference, that we have to initialize it with a value while declaring a const
variable, if we don't initialize it with a value it will throw an error.
For example
//TDZ of foo starts here
//TDZ of foo continues here
console.log(foo) //ReferenceError: foo is not defined
//TDZ of foo continues here
const foo="bar" //TDZ of foo ends here
//const foo; // will throw an error
console.log(foo) //bar
// TDZ of foo doesn't exist here
If we do console.log(foo)
before the initialization of foo
variable it will give ReferenceError
as the variable lies in TDZ.
But the second console.log(foo)
returns bar
because the TDZ of foo
variable ends when we initialize it with a value.
The reason that we get a reference error when we try to access a let
or const
variable before its declaration is because of the Temporal Dead Zone (TDZ).
JavaScript does not initialize a let
or const
variable with any value whenever it hoists the variable. Instead, the variable remains dead and inaccessible and this creates what is known as TDZ
, during which trying to access the variable results in a ReferenceError
.
TDZ of var variable
It is important to note that TDZ of var
variable works differently.
When the JS engine hoists a var
variable, it automatically initializes the variable with the value of undefined
and the TDZ of var
variable seems to be not exists.
For example
//TDZ of foo starts here and ends here
console.log(foo) //undefined
//TDZ of foo doesn't exist here
var foo="bar" //TDZ of foo doesn't exist here
//TDZ of foo doesn't exist here
console.log(foo) //bar
// TDZ of foo doesn't exist here
As you can see in the above example the first console.log
statement returns undefined
because during the compilation phase, the JS engine sees the variable declared with var
hence hoist
the variable and also initializes
it with the value of undefined
and that's the reason the TDZ
of var
variable ends in the first line of the scope.
TDZ of functions
Note that only function declarations are hoisted, not function expressions and due to which we can call/access the function declaration before the actual line of code where it is defined. But for function expression, all the rules are applicable based on its definition whether it is defined using var
let
, or const
.
The Temporal Dead Zone of a function declaration starts and ends at the beginning of the scope in which the function is defined.
For example
//TDZ of func start and end here
.
.
//TDZ of func doesn't exist here
.
.
func() //returns hello...
.
//TDZ of func doesn't exist here
.
function func(){
console.log('hello...')
}
.
//TDZ of func doesn't exist here
.
func() //returns hello...
.
.
Why do we need TDZ
- For better coding practices
The TDZ
ensures that variables are accessed only after they have been properly declared and initialized, promoting and reducing the likelihood of accessing variables in an undefined
or uninitialized state.
- To write bug-free code
The TDZ
does not apply to the variables declared with var
. They are hoisted to the top of their scope and can be accessed throughout the scope, even before their actual declaration. This behavior can sometimes lead to unexpected results and potential bugs if not handled carefully.
- More reliable and predictable code
It helps catch potential issues caused by accessing variables before their initialization.
Conclusion
Hoisting and Temporal Dead Zone are two related concepts, having knowledge of the relation between both of them and understanding and respecting the Temporal Dead Zone is an integral part of writing clean, error-free and bug-free JavaScript code. It aligns with the language's design principles and contributes to a more reliable and maintainable codebase.