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!

1
  • 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. Commented Aug 2, 2017 at 13:47

1 Answer 1

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.

5
  • Nothing more to be said: perfect answer ;-)
    – trincot
    Commented Aug 2, 2017 at 13:44
  • I edited the question to make my point clear.
    – georg
    Commented Aug 2, 2017 at 13:45
  • It is more a perfect MDN article ;)
    – Stephan
    Commented Aug 2, 2017 at 13:45
  • 1
    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.
    – Stephan
    Commented Aug 2, 2017 at 13:49
  • @Stephan: now it's perfect, many thanks!
    – georg
    Commented Aug 2, 2017 at 13:52

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.