Can someone explain me the weird error messages that occur both in chrome and firefox
when trying to access a variable before the let declaration: "let X"
. if we write something like:
console.log(X);
let X;
/*
In firefox it reports an error like:
ReferenceError: can't access lexical declaration 'X' before initialization
In chrome it reports and error like:
Uncaught ReferenceError: Cannot access 'X' before initialization
*/
why it returns an error message like you saw in the above code without giving an error message like the below ones:
console.log(X);
/* the error message i was expecting:
firefox: ReferenceError: X is not defined
chrome: ReferenceError: X is not defined
*/
does that mean let
variables hoist
too because the error messages show attributes of let variables
being hoisted means that the javaScript engine knows when we try to access a let variable
before it's declared
console.log(X);
let X;
ReferenceError: can't access lexical declaration 'X' before initialization
and please if you find any thing related to this behavior in ECMAScript specification lemme know
Yes, they "hoist" but as unaccessible thing which always throws an error when read or written. It's called the "temporal dead zone".
Further reading: https://medium.com/nmc-techblog/advanced-javascript-es6-temporal-dead-zone-default-parameters-and-let-vs-var-deep-dive-ca588fcde21b
Conceptually, for example, a strict definition of hoisting suggests that variable and function declarations are physically moved to the top of your code, but this is not in fact what happens. Instead, the variable and function declarations are put into memory during the compile phase, but stay exactly where you typed them in your code. [Hositing]
[...]
For most of the ES6 features (let, const, default parameters, etc), The Creation Phase work quite differently. It still goes through the code and allocates space for variables, but the initializer set the variables with a special mode called TDZ (Temporal Dead Zone), which means the variables exist but you can’t access them until you assign some value.
So you can imagine it like this:
let X = TDZ;
console.log(X); // error
X = undefined; // in your code: let X;
...compared to normal hosting behavior:
var X = undefined;
console.log(X); // undefined
X = whatever; // in your code: var X = whatever;
Of course this is not 100% right because you also can't write X = 123
before the let
, and there is no valid JS that would describe an "unwritable variable". But I think you get the idea.
In the ECMAScript 2021 Language Specification this is described in 13.3.1 as follows (it seems it doesn't use the term "TDZ" there, although I have heard this name used many timed before, it's also used in MDN):
13.3.1 Let and Const Declarations
NOTE
let and const declarations define variables that are scoped to the running execution context's LexicalEnvironment. The variables are created when their containing Environment Record is instantiated but may not be accessed in any way until the variable's LexicalBinding is evaluated. A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer's AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created. If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.
This means that at the start of your block, the Environment Record is instantiated - the variable "exists" but is in the TDZ. As said here, it cannot be accessed, hence the error. Once the let
line is executed, its LexicalBinding is evaluated and the variable comes out of the TDZ and is now accessible. Since you didn't specify an initializer, its value is now undefined
.