3

Please find the code in the image below. 1. Assign the returned value of a function, which throws an error actually, to the variable 'withLet' that declared by using keyword 'let'. 2. call 'withLet', an error occured: 'withLet is not defined'. 3. try to assert 'withLet' using 'let', an error shows that 'withLet' has already been declared.

But the paradox is not exist for 'var' (Please find in the following image).

I'm curious about what caused the different behaviors between these two situations. It's quite wired that 'not defined' an 'already been declared' describe a same variable.

let withLet = (function() {throw 'error!'})()
var withVar = (function() {throw 'error!'})()
//VM2470:1 Uncaught error!
//(anonymous) @ VM2470:1
//(anonymous) @ VM2470:1
withLet
//VM2484:1 Uncaught ReferenceError: withLet is not defined at 
//<anonymous>:1:1
//(anonymous) @ VM2484:1
withVar
//undefined
let withLet = 'sth'
//VM2520:1 Uncaught SyntaxError: Identifier 'withLet' has already been 
//declared
//at <anonymous>:1:1
//(anonymous) @ VM2520:1
withVar = 'sth'
//"sth"

Screenshot:

adiga
  • 34,372
  • 9
  • 61
  • 83
Heyi
  • 57
  • 6
  • 1
    Firefox just prints `undefined`. –  Jan 17 '19 at 08:28
  • Chrome throws `errors!` for each function call – Cid Jan 17 '19 at 08:28
  • i think this is a bug in the engine. `withLet` should be initialized and `undefined` but due to the nature of the variable scope it probably handles it differently. – venimus Jan 17 '19 at 08:37
  • important clarification here - this happens in Chrome/V8 but not in Firefox. So, this is implementation specific behaviour. – VLAZ Jan 17 '19 at 09:05

1 Answers1

11

Declarations of var variables get hoisted - the variable name initialization gets hoisted to the top of the containing function (or, if no function, to the top of the outer block). So

var withVar = (function() {throw 'error!'})()

is parsed by the interpreter as

var withVar;
withVar = (function() {throw 'error!'})()

The same is not true for let - let variables become initialized once the let __ line runs. When there's assignment, the right-hand side is parsed first; if the right-hand side throws an error, it never gets to the left-hand side, and the variable declared with let never gets properly initialized; it'll stay in the demilitarized zone / temporal dead zone forever, so trying to reassign it throws an error.

It's kind of weird because the code is being run in the console - ordinarily, JS runs inside a <script> tag, and if such an error occurs, usually no more code will run, and the fact that a variable declared with let is no longer reassignable is the least of your worries.


The above was an issue in earlier Chrome versions. But in Chrome 80+, re-declarations of let are now permitted, so the error

Uncaught SyntaxError: Identifier 'withLet' has already been declared

should no longer occur, regardless of whether the previous initialization of the variable succeeded or not:

enter image description here

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • 3
    Just to expand a bit - a variable has three stages to becoming "real". Declaration (that identifier exists), initialization (that idientifier is active and can be used), assignment (there is a value assigned to that identifier). For `var` the declaration + initialisation is hoisted. For `let`/`const` only the *declaration* gets hoisted, the *initialization* comes later - as stated, when the `let myVar` line is reached. – VLAZ Jan 17 '19 at 08:37