I'm attempting to understand ES6 lexical scoping (using the node runtime). Consider the following:
'use strict';
let x = 10;
function f() {
console.log(x);
console.log(y); // This should crash
}
let y = 5;
f();
Paraphrasing from the O'Reilly book "Learning Javascript":
Lexical scoping means whatever variables are in scope where you define a function from (as opposed to when you call it) are in scope in the function.
However, when I run this program (via node), it outputs: 10 5
Isn't the call to console.log(y) breaking the lexical scoping rules here? If not, why not?
Edit: For future reference, it appears that the author of the textbook (Learning Javascript 3rd Edition O'Reilly) has recently listed this example as an error in the "Confirmed Errata". on http://www.oreilly.com/catalog/errata.csp?isbn=0636920035534
As mentioned by Benjamin Gruenbaum, let
and const
don't hoist at all.
As a matter of fact, there are new rules that apply to let
and const
, such as the…
temporal dead zone
Now, if these were var
declarations, everything would be clear. But with let
and const
, ES6 introduces a new concept of the temporal dead zone. This includes a new, subtle dynamic.
Let's have a look at two examples:
Hoisting classically would work in an example like this:
'use strict';
var x = 10;
console.log(x);
console.log(y); // This should NOT crash
var y = 5;
But if we were to replace the var
declarations with let
declarations, it would crash:
'use strict';
let x = 10;
console.log(x);
console.log(y); // This crashes: ReferenceError: can't access lexical declaration `y' before initialization
let y = 5;
Why does this crash?
Because unlike var
assignments, accessing variables defined using let
before the actual let
statement is invalid (they are in the temporal dead zone).
2. Temporal Dead Zone in this case
In this case however, the temporal dead zone is not an issue. Why?
Because while we define the function with the console.log(y)
statement beforehand, the actual function call and thus variable access only happens towards the end of the code. So the variable bindings are only evaluated at this point (thanks again, @BG):
'use strict';
let x = 10;
function f() {
console.log(x);
console.log(y); // This should not yet crash
}
let y = 5;
f(); // console.log(y) is only called here
If you were to reverse the order of let y = 5;
and f();
, your code would crash.