liveBook · Manning

Scope

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

A scoping rule in which a binding is visible only within the block in which it is declared and nested blocks inside it.

Why can a variable used inside a loop cause trouble elsewhere? Scope determines where a binding can be accessed. With `let`, a binding is visible in its containing block and nested blocks, but not outside. A block is the area between `{` and `}`; it can even stand on its own. section 4.1

A boundary you can see

if (true) { let foo = 'bar'; console.log(foo); // 'bar' } console.log(foo); // ReferenceError The first lookup is inside `foo`’s block. The second is outside it. By contrast, a `var` declared in the `if` block remains accessible elsewhere in its containing function. section 4.1

A `let` declared in a `for` clause belongs to the loop’s block: the body can use it, but code after the loop cannot. This differs from `var`’s function scope. section 4.1 section 4.4

Listing 4.2. There is a scope problem here, but what is it?
<ul> <li>one</li> <li>two</li> <li>three</li> <li>four</li> <li>five</li> </ul> ... <script type="javascript"> var items = document.querySelectorAll('li'); 1 for (var i = 0; i < 5; i++) { var li = items[i]; li.addEventListener('click', function() { 2 alert(li.textContent + ':' + i); }); }; </script>

In the list-item click-handler example, `var` leaves the loop’s `li` and `i` bindings shared across handlers. Check their values when a click actually occurs, not just when the handler is attached. section 4.1

Listing 4.1. Using a free-standing block to keep a variable private
let read, write; { 1 let data = {}; 2 write = function (key, val) { data[key] = val; } read = function (key) { return data[key]; } } 3 write('message', 'Welcome to ES6!'); read('message'); 4 console.log(data); 5

A free-standing block keeps `data` inaccessible from outside, while the `read` and `write` functions created inside can continue to use it. Block scope can protect a working variable without wrapping the block in another function. section 4.1

Scope does not make early access safe: reading a `let` binding before its declaration throws, whereas an early read of `var` yields `undefined`. When changing existing code, do not replace every `var` mechanically; first check where each binding is used. section 4.3

Quick practice

In a range-generating function, identify the temporary swap value and loop counter. Put each `let` declaration in the block where it is needed, then check whether the IIFEs formerly used to hide those variables are still necessary. section 4.4

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

How deep it goes
Defines block and function scope, works through block and loop boundaries, and contrasts early access without detailing runtime internals.
Examples
Uses an if block, a private-data block, list-item click handlers, and a range-function rewrite.
What it assumes
Assumes familiarity with JavaScript variables, functions, and loops while defining block and function scope explicitly.
What you can do after
Apply it with let to confine temporary and loop variables and remove unnecessary IIFEs from a range function.
How it connects
Connects scope to let, var, for loops, closures, hoisting, and IIFEs.

Also explained in

Compare how 21 books explain scope →

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.