This concept is probably not something that you come across too often and sounds a little bit weird. But it can be useful to know some details to avoid possible bugs or problems in your code.
So, let's look at the example below. It's easy to see that it will print foo
to console.
var foo = 'foo';
console.log(foo); // foo
What if we change the order of the lines as below:
console.log(foo);
var foo = 'foo';
Now you will see that it prints undefined
. This is because the var
declaration is hoisted but the value of variable is undefined when console.log line is executed.
Now let's add a bit of ES6 to our simple example.
console.log(foo);
let foo = 'foo';
If you run this code on your console, you will get a ReferenceError
. This is one of the main differences between var
and let
/const
. When let
/const
variables are accessed before their declaration, they throw a ReferenceError instead of having undefined value.
You may think that let
/const
variables are not hoisted and we're getting ReferenceError because of this, but this is not correct.
Let's explain that in a little bit more complex example.
var foo = 'first';
function example() {
console.log(foo);
var foo = 'second';
}
example();
What do you think the code above will print to console?
The answer is undefined
. This is a simple example that explains how hoisting works in JavaScript. foo
variable is hoisted inside of the function scope but it's not initialized where the console.log line is executed, so it prints undefined
. This is the expected result.
So what if we change it as below?
let foo = 'first';
function example() {
console.log(foo);
let foo = 'second';
}
example();
This code above will throw a ReferenceError
similar to the let
example before. This is because let
/const
variables are actually hoisted, but they throw ReferenceError
if they're accessed before being initialized. The variable is in a "temporal dead zone" from the start of the block until the initialization is processed.
If you want to dig deeper you can see MDN documentation of let
here, and ES specification here.
There is a misconception that says let/const are not hoisted in JavaScript, we cannot say that is correct. According to ES6 specification: The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable’s LexicalBinding is evaluated.
Note: You can see another great explanation of hoisting of let/const here.
You can also read this post in my blog.
Top comments (2)
Honestly this is part of why I think let/const just takes something already painfully convoluted and annoying, and just makes things worse. If you need scoping beyond the function level, or have trouble maintaining declaration orders, your code is probably crap to begin with.
But what do I know? I come from a "Wirth family" language background (Ada, Pascal, Modula) so to me any language that doesn't make you use forward declaration is junk.
Let's be honest, the ONLY reason any of us actually use JS by choice is the death-grip it has on the browser, and some people's unwillingness to learn more than one language so they shoe-horn it into the server-side.
I agree that sometimes let/const makes things a little bit complicated. But it's good to solve some of weird block behavior of JavaScript, the only burden is that you have to learn how and where to use it to get this advantage.
I come from a Java background but I love coding in JavaScript. I think comparing JavaScript with other languages is an endless discussion. So, I just love good parts of the languages which I use and try to avoid bad practices as much as possible.