liveBook · Manning

Variable declaration keyword

As explained in Get Programming with JavaScript Next: New features of ECMAScript 2015, 2016, and beyond

A JavaScript keyword that declares a block-scoped local variable whose binding cannot be accessed outside the block where it is declared.

Why can a loop variable unexpectedly still be available after a loop—or make every delayed callback print the same value? In JavaScript, `let` declares a variable scoped to its containing block, rather than to the containing function as `var` does. A block is the region between `{` and `}`; a free-standing pair of braces can create one, too. section 4.0 section 4.1

A variable that stays inside its block

`if (true) { let message = 'hello'; console.log(message); }` prints `hello`. A following `console.log(message)` throws a reference error: the binding does not extend beyond the `if` block. In `for (let i = 0; i < 5; i++)`, `i` is likewise available in the loop body but not after the loop, even though its declaration appears in the loop clause. section 4.1

for (var i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i);
  }, 1);
};
for (let n = 0; n < 5; n++) {
  setTimeout(function () {
    console.log(n);
  }, 1);
};

Compare the two delayed-callback loops. The `var` loop’s callbacks read its shared, function-scoped iterator after the loop finishes, so each prints `5`. The `let` loop gives each iteration its own value, producing `0` through `4`. section 4.0 section 4.1

Block scope can also keep working data out of surrounding code. Declare `data` inside a free-standing block and assign `read` and `write` functions there. Those functions can continue using `data`, while code outside the block cannot reference it directly. With `var`, an immediately invoked function expression (IIFE) can provide a similar boundary; `let` often lets the block itself do that job. section 4.1

In scope does not mean ready to read

`{ console.log(value); let value = 2; }` throws a reference error at the first line. The `let` binding belongs to the entire block, but reading it before its declaration falls in the temporal dead zone. By contrast, reading a `var` binding before its declaration yields `undefined`. An inner `let` also shadows an outer variable with the same name throughout the inner block; an early read does not fall back to the outer value. section 4.2

Use `let` when a variable needs to change but should remain local to a block. Its predeclaration error can expose a mistake that `var` would leave as an `undefined` read. Do not blindly replace every `var` in existing code: changed scope and early-read behavior can introduce errors. section 4.3

(function (namespace) {

  var DEFAULT_START = 0;
  var DEFAULT_STEP  = 1;
  var range = function (start, stop, step) {
    var arr = [];

    if (!step) {
      step = DEFAULT_STEP;
    }

    if (!stop) {
      stop = start;
      start = DEFAULT_START;
    }

    if (stop < start) {
      (function () {
        // reverse values
        var tmp = start;
        start = stop;
        stop = tmp;
      }());
    }

    (function () {
      var i;
      for (i = start; i < stop; i += step) {
        arr.push(i);
      }
    }());

    return arr;
  }

  namespace.range = range;

}(window.mylib));

Practice by rewriting the range function with `let` so the temporary swap value and loop iterator stay local without their surrounding IIFEs. Check which declarations still need to be visible throughout the function. section 4.4

How Get Programming with JavaScript Next: New features of ECMAScript 2015, 2016, and beyond covers it

How deep it goes
Explains block scope, loop behavior, hoisting, and temporal-dead-zone errors through examples, stopping at observable behavior rather than engine internals.
Examples
Contrasts delayed loop callbacks, hides data in a free-standing block, and challenges readers to remove IIFEs from a range function.
What it assumes
Assumes familiarity with JavaScript variables, functions, and loops while defining blocks and explaining their scope rules.
What you can do after
Practice/perform block-scoped declarations in loops and replace IIFEs used only to contain local variables.
How it connects
Connects `let` to `var`, function scope, closures, IIFEs, hoisting, and the temporal dead zone.

Also explained in

Compare how 2 books explain variable declaration keyword →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Get Programming with JavaScript Next: New features of ECMAScript 2015, 2016, and beyond. The section links point to the parts of the book it draws on.