4

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

5
  • 1
    Why wouldn't it hoist? It's a variable just like a var. It just has a reduced scope
    – Taplar
    Commented Jun 5, 2020 at 14:55
  • so then why people call that let variables never hoist
    – Kevin
    Commented Jun 5, 2020 at 14:56
  • 1
    What they mean is that you cannot use them before defined. As you see: Trying to do so throws an error.
    – CherryDT
    Commented Jun 5, 2020 at 15:11
  • 2
    However, an annoying side effect is of course that while they exist in their unassigned form, they can still shadow variables from outer scopes. So adding a let location in the last line of your function can still break your whole function's previous code, but at least it will do so in a more meaningful way. If you'd put var location into the last line of your function and your first line would have done location = 'http://yahoo.com', then it would have silently broken the code, but with let it does so in a noticable way.
    – CherryDT
    Commented Jun 5, 2020 at 15:14
  • 2
    I realize there is actually a better answer already on SO: What is the temporal dead zone?
    – CherryDT
    Commented Jun 5, 2020 at 15:16

1 Answer 1

2

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.

3
  • is this mentioned in tc39.es/ecma262 ECMAscript specs by the way thanks you saved me
    – Kevin
    Commented Jun 5, 2020 at 15:03
  • 1
    Added it to my answer.
    – CherryDT
    Commented Jun 5, 2020 at 15:10
  • Thank you very much
    – Kevin
    Commented Jun 5, 2020 at 15:20

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.