I used to think that hoisting only happened to variables declared with var
. But recently, I learned that it also happens to variables declared with let
and const
.
I'll explain what I mean in this article.
I also have a video version of this article you can check out if you're interested.
How Hoisting Works with var
in JavaScript
Here's how hoisting works on variables declared with var
:
console.log(number)
// undefined
var number = 10
console.log(number)
// 10
The number
variable is hoisted to the top of the global scope. This makes it possible to access the variable before the line it was declared, without errors.
But what you'll notice here is that only the variable declaration (var number
) is hoisted – the initialization (= 10
) isn't. So when you try to access number
before it is declared, you get the default initialization that happens with var which is undefined
.
Then, the declaration and initialization line is executed, so accessing number
after that returns the initialized value, 10.
How Hoisting Works with let/const in JavaScript
If you try to do the same thing as above with let
or const
, here's what happens:
console.log(number)
let number = 10
// or const number = 10
console.log(number)
You get an error that says: ReferenceError: Cannot access 'number' before initialization.
So you can access a variable declared with var before declaration without errors, but you cannot do the same with let
or const
.
This why I had always thought that hoisting only happens with var, it doesn't happen with let or const.
But as I said, I learned recently that variables declared with let
or const
are also hoisted. Let me explain.
Take a look at this example:
console.log(number2)
let number = 10
I log a variable called number2
to the console, and I declare and initialize a variable called number
.
Running this code produces this error: ReferenceError: number2 is not defined
What do you notice between the previous error and this error? The previous error says ReferenceError: Cannot access 'number' before initialization while this new error says ReferenceError: number2 is not defined.
Here's the difference. The former says "cannot access before initialization" while the latter says "is not defined".
What the latter means is that JavaScript has no idea what the number2
variable is because it is not defined – and indeed we didn't define it. We only defined number
.
But the former doesn't say "is not defined", instead it says, "cannot access before initialization". Here's the code again:
console.log(number)
// ReferenceError: Cannot access 'number' before initialization
let number = 10
console.log(number)
This means that JavaScript "knows" about the number
variable. How does it know? Because number
is hoisted to the top of the global scope.
But why does an error occur? Well, this clarifies the difference between the hoisting behavior with var
and let
/const
.
Variables declared with let
or const
are hoisted WITHOUT a default initialization. So accessing them before the line they were declared throws ReferenceError: Cannot access 'variable' before initialization.
But variables declared with var
are hoisted WITH a default initialization of undefined. So accessing them before the line they were declared returns undefined
.
Temporal Dead Zone
There's a name for the period during execution where let
/const
variables are hoisted but not accessible: it's called the Temporal Dead Zone.
Again, the code from above:
console.log(number)
let number = 10
console.log(number)
The number
variable is in a temporal dead zone where JavaScript knows of its existence (because its declaration is hoisted) but it's not accessible (as it doesn't have an initialization).
Wrapping Up
If you were like me, and you thought that hoisting only applies with var
and not let
/const
, I hope this article clears up that false assumption.
As I've explained in this article, let
and const
variables are hoisted, only they are hoisted without a default initialization. This makes them inaccessible (as such variables are in a temporal dead zone).
Variables declared with var
, on the other hand, are hoisted with a default initialization of undefined
.
I hope you learned something from this article :)