Please explain the use of JavaScript closures in loops

34,822

Solution 1

WARNING: Long(ish) Answer

This is copied directly from an article I wrote in an internal company wiki:

Question: How to properly use closures in loops? Quick answer: Use a function factory.

  for (var i=0; i<10; i++) {
    document.getElementById(i).onclick = (function(x){
      return function(){
        alert(x);
      }
    })(i);
  }

or the more easily readable version:

  function generateMyHandler (x) {
    return function(){
      alert(x);
    }
  }

  for (var i=0; i<10; i++) {
    document.getElementById(i).onclick = generateMyHandler(i);
  }

This often confuse people who are new to javascript or functional programming. It is a result of misunderstanding what closures are.

A closure does not merely pass the value of a variable or even a reference to the variable. A closure captures the variable itself! The following bit of code illustrates this:

  var message = 'Hello!';
  document.getElementById('foo').onclick = function(){alert(message)};
  message = 'Goodbye!';

Clicking the element 'foo' will generate an alert box with the message: "Goodbye!". Because of this, using a simple closure in a loop will end up with all closures sharing the same variable and that variable will contain the last value assigned to it in the loop. For example:

  for (var i=0; i<10; i++) {
    document.getElementById('something'+i).onclick = function(){alert(i)};
  }

All elements when clicked will generate an alert box with the number 10. In fact, if we now do i="hello"; all elements will now generate a "hello" alert! The variable i is shared across ten functions PLUS the current function/scope/context. Think of it as a sort of private global variable that only the functions involved can see.

What we want is an instance of that variable or at least a simple reference to the variable instead of the variable itself. Fortunately javascript already has a mechanism for passing a reference (for objects) or value (for strings and numbers): function arguments!

When a function is called in javascript the arguments to that function is passed by reference if it is an object or by value if it is a string or number. This is enough to break variable sharing in closures.

So:

  for (var i=0; i<10; i++) {
    document.getElementById(i).onclick =
      (function(x){ /* we use this function expression simply as a factory
                       to return the function we really want to use: */

        /* we want to return a function reference
           so we write a function expression*/
        return function(){
          alert(x); /* x here refers to the argument of the factory function
                       captured by the 'inner' closure */
        }

      /* The brace operators (..) evaluates an expression, in this case this
         function expression which yields a function reference. */

      })(i) /* The function reference generated is then immediately called()
               where the variable i is passed */
  }

Solution 2

I've been programming in JavaScript for a long time, and "closure in a loop" is a very broad topic. I assume you are talking about the practice of using (function(param) { return function(){ ... }; })(param); inside of a for loop in order to preserve the "current value" of the loop when that inner function later executes...

The code:

for(var i=0; i<4; i++) {
  setTimeout(
    // argument #1 to setTimeout is a function.
    // this "outer function" is immediately executed, with `i` as its parameter
    (function(x) {
      // the "outer function" returns an "inner function" which now has x=i at the
      // time the "outer function" was called
      return function() {  
        console.log("i=="+i+", x=="+x);
      };
    })(i) // execute the "closure" immediately, x=i, returns a "callback" function
  // finishing up arguments to setTimeout
  , i*100);
}

Output:

i==4, x==0
i==4, x==1
i==4, x==2
i==4, x==3

As you can see by the output, all of the inner callback functions all point to the same i, however, since each had its own 'closure', the value of x is actually stored as whatever i was at the time of the outer function's execution.

Commonly when you see this pattern, you would use the same variable name as the parameter and the argument to the outer function: (function(i){ })(i) for instance. Any code inside that function (even if executed later, like a callback function) is going to refer to i at the time you called the "outer function".

Solution 3

Well, the "problem" with closures in such a case is, that any access to i would reference the same variable. That is because of ECMA-/Javascripts function scope or lexical scope.

So to avoid that every call to alert(i); would display a 5 (because after the loop finished i === 5), you need to create a new function which invokes itself at runtime.

To achieve this, you need to create a new function, plus you need the extra paranthesis at the end, to invoke the outer function immediately, so link.onclick has now the returned function as reference.

Solution 4

A closure is a construct in which you reference a variable outside the scope in which it's defined. You usually talk about closures in the context of a function.

var helloFunction;
var finished = false;

while (!finished) {
 var message = 'Hello, World!';
 helloFunction = function() {
   alert(message);
 }
 finished = true;
}

helloFunction();

Here, I define the variable message, and define a function that references message. When I define the function to use message, I am creating a closure. This means helloFunction holds a reference to message, so that I can continue to use message, even outside of the scope (the loop body) where message is defined.

Addendum

The (i) in parenthesis is a function call. What's happening is:

  1. You define some function(num) {}. This is called an anonymous function, because it's defined inline and doesn't have a name.
  2. function(num) takes an integer argument, and returns a reference to another function, which is defined as alert(num)
  3. The outer anonymous function is immediately called, with the argument i. So num=i. The result of this call is a function which will do alert(i).
  4. The end result is more or less equivalent to: link.onclick = function() { alert(i); };
Share:
34,822
CMS scripting
Author by

CMS scripting

Looking for some education Made my way into the night All that bullshit, conversation Baby can't you read the signs?

Updated on October 28, 2020

Comments

  • CMS scripting
    CMS scripting over 3 years

    I have read a number of explanations about closures and closures inside loops. I have a hard time understanding the concept. I have this code: Is there a way to reduce the code as much as possible so the concept of closure can be made clearer. I am having a hard time understanding the part in which the i is inside two parenthesis. Thanks

    function addLinks () {
        for (var i=0, link; i<5; i++) {
    
            link = document.createElement("a");
            link.innerHTML = "Link " + i;
    
    
            link.onclick = function (num) {
                return function () {
                    alert(num);
                };
            }(i);
            document.body.appendChild(link);
    
        }
    }
    window.onload = addLinks;