JavaScript Closures - Part 1

When people speak about closures in JavaScript they are normally refering to nested functions maintaining references to variables from an outer function.

Listing 1


function outerFunction() {
  // declare a 'private' variable
  var a = 10;
  
  // create a function that accesses the variable
  function innerFunction() {
    return a;
  }
  
  // pass the ability to access the variable out of the function
  return innerFunction;
}

var getA = outerFunction();
alert(getA()); // 10

When outerFunction is called, it executes each statement in its definition and then ends. Normally, all variables within the function would then disappear. However, in the example, innerFunction is passed out and assigned to a variable getA. Now, when getA() invokes innerFunction, innerFunction returns the value of a which is 10. This happens even though outerFunction has finished executing.

When the nested function innerFunction refers to the variable a in the outer function, a closure is formed allowing innerFunction to maintain its reference after outerFunction completes execution.

The Big Mistake

A very common mistake when programming JavaScript is to not realise (or forget) that the closure allows the nested function access to the value of variables when the nested function is invoked.

Listing 2


function outerFunction() {
  // declare a 'private' variable
  var a = 10;
  
  // create a function that accesses the variable
  function innerFunction() {
    return a;
  }
  
  // change the value of a, after innerFunction is defined
  a = 51;
  
  // pass the ability to access the variable out of the function
  return innerFunction;
}

var getA = outerFunction();
alert(getA()); // 51, the last value of a

Once again, innerFunction is assigned to the variable getA. When the function is called it returns the value of a at the time of invocation, which is 51, the last value assigned to a in the outer function.

This mistake is often seen when assigning event handlers within loops. As an example, say we have three buttons and want to assign handlers for their click events. We obtain a reference to the three buttons as a node list (like an array but without the built in methods) and assign it to the variable btns. We then loop over the list and assign a function to each handler which should alert the value of the loop variable i for each button.

Listing 3


<div id="buttons">
<input type="button" value="Show Index 0" />
<input type="button" value="Show Index 1" />
<input type="button" value="Show Index 2" />
</div>

<script type="text/javascript">
var container = document.getElementById("buttons");
var btns = container.getElementsByTagName("input");

for (var i = 0, len = btns.length; i < len; i++) {
  btns[i].onclick = function() {
    alert(i);
  };
}
</script>

And there's the mistake! Notice the buttons each report a value of 3. But why?

The onclick handlers are assigned functions that are nested within the main page script. Each function includes the statement alert(i); which maintains a reference to the loop variable i which is outside of the function. So each function is part of a closure including the variable i. The loop runs through all of its values, ending with i = 3, the value that halts the loop. When the buttons are clicked, the onclick handler is called and reports the value of i at the time the button is clicked, namely 3.

A Solution

The event handler functions need to use the value of i at the time of assignment rather than when they are called. This can be achieved by wrapping the loop block in an anonymous function that is called immediately.

Listing 4


<div id="buttons">
<input type="button" value="Show Index 0" />
<input type="button" value="Show Index 1" />
<input type="button" value="Show Index 2" />
</div>

<script type="text/javascript">
var container = document.getElementById("buttons");
var btns = container.getElementsByTagName("input");

for (var i = 0, len = btns.length; i < len; i++) {
  (function(n){
    btns[n].onclick = function() {
      alert(n);
    };
  })(i);
}
</script>

For each iteration of the loop, the value of i is passed into a new function and is assigned to the parameter n. The event handler now maintains a reference to n in a closure. But the new wrapper function immediately ends execution, so the event handler keeps its reference to the value that was passed in. Each iteration of the loop executes its own version of the wrapper function; each event handler points to a different version of n.

The loop can be broken down to show what is happening:

Listing 5


// Start of loop
// i = 0

  (function(n){
    btns[n].onclick = function() {
      alert(n);
    };
  })(0); // 0 passed into the function
  
// i = 1

  (function(n){
    btns[n].onclick = function() {
      alert(n);
    };
  })(1); // 1 passed into the function
  
// i = 2

  (function(n){
    btns[n].onclick = function() {
      alert(n);
    };
  })(2); // 2 passed into the function
  
// i = 3
// Loop ends

Each event handler gets a different value of i, rather than a reference to i and the desired outcome is achieved.

Dr JavaScript

Dr JavaScript Comic