In this post, I will be talking about hoisting, temporal dead zone, and how hoisting works with let and const.
What is hoisting?
The JavaScript engine before parses the code before executing and during the parsing phase it shifts all the variable declaration to the top of the scope. This behavior of the JS engine is called hoisting.
Variable Hoisting
Consider the following code snippet -
console.log(greeting); // undefined
var greeting = "Hello";
We can see that the greeting
variable can be accessed before its declared. This happens because the JS engine modifies our code snippet into something like this -
var greeting;
console.log(greeting); // undefined
var greeting = "Hello";
Function Hoisting
The formal function declarations in JavaScript are also hoisted to the top of the scope. For example:
greeting(); // Hello
function greeting() {
console.log("Hello");
}
Note: The important distinction between variable hoisting and function hoisting is that a var
variable is hoisted and then auto-initialized to undefined
whereas a function declaration is hoisted and initialized to its function value.
Function declaration vs Function expression
Function hoisting only applies to formal function
declarations and not to function
expression assignments. Consider:
greeting(); // TypeError: greeting is not a function
console.log(greeting); // undefined
var greeting = function greeting() {
console.log("Hello!");
};
Above, we can see that the greeting
variable was hoisted but it was not initialized with the function reference. The engine throws us a TypeError: greeting is not a function
and not ReferenceError: greeting is not defined
. The function expression assignments behave very much like variable hoisting.
What about let and const?
So far, I have only talked about var
and formal function
declarations. What about the let
and const
. Let's see the following code snippet -
console.log(greeting); // cannot access 'greeting' before initialization
let greeting = "Hello";
We get a new kind of error, its not a ReferenceError
, the engine knows about greeting
but doesn't allow us to use it before its initialized. The JS engine doesn't allow us to access the variables declared with let
and const
before they are declared. This is called Temporal Dead Zone.
Let's consider this snippet -
let greeting;
console.log(greeting); // undefined
greeting = "Hello";
Above, we can see that we are able to access the greeting
variable as soon as it was declared.
So, let and const are not hoisted?
After seeing the above two code snippets, I was pretty convinced too that let
and const
are not hoisted. But they actually are. We can prove this with the help of a few more examples -
console.log(typeof iDontExist); // undefined
console.log(typeof greeting); // cannot access 'greeting' before initialization
let greeting = "hello";
If the greeting
variable was not hoisted, we would expect typeof greeting
to be undefined
similar to typeof iDontExist
. This proves that the JS engine knows about our greeting
variable but still doesn't allow us to access it just yet due to Temporal Dead Zone.
Let's see another example -
let x = 'outer value';
console.log(x); // outer value
{
// start TDZ for x
console.log(x); // cannot access 'x' before initialization
let x = 'inner value'; // declaration ends TDZ for x
}
Accessing the variable x
in the inner scope still causes the TDZ error. If the let x = 'inner value';
was not hoisted then on line 6, it would have logged outer value
.
Conclusion
- The
var
declarations are hoisted and initialized withundefined
. - The formal function declarations are hoisted and initialized with their function reference.
-
let
andconst
variables are hoisted too but they cannot be accessed before their declarations. This is called Temporal Dead Zone.
Top comments (1)
Thanks for the explanations. Very helpful summary.