3

This code works fine:

const a = 1;
{
  const b = a; // let's use "a" in a sub-scope
}
{
  const a = 2; // let's override "a" in another sub-scope
}

... but this code fails with a is not defined at line 3:

const a = 1;
{
  const b = a; // let's use "a" in a sub-scope <<< CRASH!! >>>
  const a = 2; // let's override "a" in the same sub-scope
}

I tested this on node 7.7.4. It may be related to V8 only, I'm curious how Firefox would handle this.

Is it related to the way variable declaration is defined in the JS spec? const/let are not hoisted, but "something" (some metadata?) still seems to be hoisted, else we would have no issues.

Aurelien Ribon
  • 7,548
  • 3
  • 43
  • 54
  • 1
    **All** declarations are hoisted in JavaScript, to the top of their scope. For `const` and `let`, that's the scoping block. – Pointy Apr 01 '17 at 13:15

2 Answers2

2

Hoisting refers to the notion that variable declarations are hoisted a to the top of the variable scope.

More accurately, it is a side effect of the a fact that JavaScript builds it's execution environment in two passes.

The first pass sets up all function and variable declarations​. At this stage, variables are declared but not defined, function declarations are declared and defined (that's function declarations: function fnName(){...} not variable function expressions: var fnName = function (){...}).

The second pass then executes the code with the available declared functions and variables (which currently are unassigned and therefore have the value undefined).

var is function scoped and not block scoped. Although let andconst allow the declaration of block scoped variables, the code is built using the two execution passes. The difference being that block scope should be considered in the first pass within their own scope (block scope or otherwise).

To illustrate this point, consider your second snippet.

The code throws an error in your second case because after the first pass it could be re-written as:

// const 'a' declared in this outer scope
const a:
a = 1; 

{ 
  // separate const 'a' declared in this block scope
  // Declarations still hoisted for this block scope
  const b, a
  b = a; // a undefined here
  a = 2;
}
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Pineda
  • 7,435
  • 3
  • 30
  • 45
1

This is according to the ES6 spec for variable declarations using let and const. They are both Block Scoped.
The time before the declaration within the scope is temporal dead zone. According to the docs:

In ECMAScript 2015, let will hoist the variable to the top of the block. However, referencing the variable in the block before the variable declaration results in a ReferenceError. The variable is in a "temporal dead zone" from the start of the block until the declaration is processed.

As @Bergi mentioned it is the time before the actual declaration.

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
  • Thanks! Indeed, I stumbled on the same article and I was pleased that it describes the TDZ so well. I'm a bit sad, since my example is legit in Java, C++ and other mainstream languages. I thought that `let/const` would let JS get closer to them syntax-wise. Good to know thought. – Aurelien Ribon Apr 01 '17 at 13:22
  • 1
    It's called *temporal* dead zone because it's not an "area" but rather a time period. The "before" is not meant spatially. – Bergi Apr 01 '17 at 13:23