liveBook · Manning

Closure

As explained in Secrets of the JavaScript Ninja, Second Edition

A function together with the lexical environment in which it was created, allowing it to access variables from that environment after the outer function has finished executing.

Need a timer callback to remember which element it is animating? Keep that state local rather than putting it in global variables. A closure lets a function access variables that were in scope when the function was created—even after the surrounding function has returned. section 5.2.2 section 5.7

How it works: a function retains a reference to the lexical environment in which it was created. Calling an outer function creates a new environment for that call. An inner function that remains accessible can continue using its variables after the outer call's execution context has left the stack. section 5.6.1 section 5.7

Keep a count behind methods

A constructor declares a local count, then creates one method to increment it and another to read it. Each new instance gets methods that close over its own count. Callers can use those methods, but cannot read the constructor's local variable as an object property. section 5.2.1 section 5.6.1

Listing 5.11. Approximate private variables with closures
function Ninja() { 
  var feints = 0;                                        #A
  this.getFeints = function(){                           #B
    return feints;                                       #B
  };                                                     #B
  this.feint = function(){                               #C
    feints++;                                            #C
  };                                                     #C
}   
var ninja1 = new Ninja();  
assert(ninja1.feints === undefined,                       #D
       "And the private data is inaccessible to us.");    #D
ninja1.feint();                                             #E
assert(ninja1.getFeints()=== 1,                             #F
       "We're able to access the internal feint count.");   #F
  
var ninja2 = new Ninja();                                   #G
assert(ninja2.getFeints()=== 0, 
       "The second ninja object gets its own feints variable.");

Follow the getter and increment method: both use a variable declared in their constructor, while two constructed objects retain separate counts. section 5.6.1

That privacy is an approximation, not a property of the object. Assign the getter to another object and it still reads the variable from the environment where the function was created. The access travels with the function. section 5.6.2

Listing 5.13. Using a closure in a timer interval callback
<div id="box1">First Box</div>
<div id="box2">Second Box</div>
<script>
  function animateIt(elementId) {
    var elem = document.getElementById(elementId);
    var tick = 0;
    var timer = setInterval(function(){
      if (tick < 100) {
        elem.style.left = elem.style.top = tick + "px";
        tick++;
      }
      else {
        clearInterval(timer);
        assert(tick === 100,
               "Tick accessed via a closure.");
        assert(elem,
               "Element also accessed via a closure.");
        assert(timer,
               "Timer reference also obtained via a closure." );
      }
    }, 10);
  }
  animateIt("box1");
  animateIt("box2");
</script>


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":0},{\"line\":0,\"ch\":30}],[{\"line\":1,\"ch\":0},{\"line\":1,\"ch\":31}],[{\"line\":22,\"ch\":2},{\"line\":22,\"ch\":20}],[{\"line\":23,\"ch\":2},{\"line\":23,\"ch\":20}],[{\"line\":24,\"ch\":0},{\"line\":24,\"ch\":9}]]"}
!@%STYLE%@!

Two timer-driven animations each retain their own element, tick count, and timer reference without needing separate global variables. section 5.6.3

The same idea supports modules: an immediately invoked function keeps implementation variables local, then returns an object whose methods can still use them. Closures connect a public interface to state that outside code cannot access directly. section 11.1.1

Quick checks:

  • Where was the function created? That determines the environment it retains. section 5.6.1
  • Does each operation need independent state? Put it inside each outer call rather than sharing globals. section 5.2.2

How Secrets of the JavaScript Ninja, Second Edition covers it

How deep it goes
Explains closures through worked examples, then traces retained lexical environments and execution contexts; qualifies the private-variable approximation.
Examples
Uses constructor-held counters, independently animated DOM elements, reassigned methods, and a click-counting module.
What it assumes
Assumes familiarity with JavaScript functions and constructors while explaining lexical environments and execution contexts for closure mechanics.
What you can do after
Build it from scratch: isolate callback state, approximate private variables, and expose module methods without exposing internal variables.
How it connects
Connects closures to lexical scope, execution contexts, callbacks, constructors, private variables, and the module pattern.

Also explained in

Compare how 20 books explain closure →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Secrets of the JavaScript Ninja, Second Edition. The section links point to the parts of the book it draws on.