2

Consider this:

'use strict';

{
  let p = 1;
  {
    console.log(p); 
    let p = 2;
  }
}

A gut feeling tells us it should log "1" (since the var must retain its old value before redeclared). However, the actual result is a ReferenceError. Why is that? (A standard-based explanation will be appreciated).

Please do note that I already have p declared in the outer scope, so it's known in the inner block. If you comment out the p=2 line, everything works just fine.

As a post mortem note, although this behavior appears to be documented, it's still very counter-intuitive, cf. this C code:

void main() {
  int p = 1;
  {
    printf("%d\n", p); // prints '1'
    int p = 2;
  }
}

Yet another JS fuckup peculiarity to make a note of!

georg
  • 211,518
  • 52
  • 313
  • 390
  • 3
    _"If you comment out the p=2 line, everything works just fine."_ Well of course it does... you don't have a conflicting block scoped variable then. – James Thorpe Aug 02 '17 at 13:47

1 Answers1

12

According to MDN:

In ECMAScript 2015, let bindings are not subject to Variable Hoisting, which means that let declarations do not move to the top of the current execution context. Referencing the variable in the block before the initialization results in a ReferenceError (contrary to a variable declared with var, which will just have the undefined value). The variable is in a "temporal dead zone" from the start of the block until the initialization is processed.

The problem is that your let p statement creates a new variable, whose scope is the whole code block. Therefore the p in console.log(p); is referring to the newly created variable.

An example similar to your situation is provided:

function test(){
   var foo = 33;
   if (true) {
     let foo = (foo + 55); // ReferenceError
   }
}
test();

Due to lexical scoping, the identifier "foo" inside the expression (foo + 55) evaluates to the if block's foo, and not the overlying variable foo with the value of 33. In that very line, the if block's "foo" has already been created in the lexical environment, but has not yet reached (and terminated) its initialization (which is part of the statement itself): it's still in the temporal dead zone.

Stephan
  • 2,028
  • 16
  • 19