views:

124

answers:

5

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;
+1  A: 

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); };
RMorrisey
Wow that was a fast answer, and it's my first question. Thank you
Camilo Sanchez
You're welcome! If that solved your problem, please mark the answer as accepted by clicking the checkmark. Stackoverflow is great for getting answers quickly =)
RMorrisey
+2  A: 

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".

gnarf
A: 

To answer the last part of your questions. The two parenthesis invoke the function as any other functions. Why you do it here is that you want to keep what the variable "i" is just at that time. So what it does is, invoke the function, the i is sent as a argument "num". Since it's invoke it will remember the value nume in variable links own scoop.

If you did't to this all link click would result in an alert saying "5"

John Resig, founder of jQuery, has a really nice online presentation explaining this. http://ejohn.org/apps/learn/

..fredrik

fredrik
+1  A: 

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.

jAndy
+4  A: 

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 9. In fact, if we now do i="hello"; all elements will now generate a "hello" alert! The variable i is shared accross 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 */
  }
slebetman
To the OP: I started typing before you added your example code. So I'm using my own example code. Hope you don't mind.
slebetman
"A closure does not merely pass the value of a variable or even a reference to the variable. A closure captures the variable itself!"I like this explanation.
Q_the_dreadlocked_ninja
Thank you so much to all for the help. As it turns out one of the reason why I couldn't grasp the concept of closures and especially the (i) function, is because I lacked the understanding of anonymous functions being called in such manner. See, I was so used to the : myfunction() way of doing it, that calling a function just like () made things really confusing to me. So if anyone is confused about that parenthesis think of the concept myfunction(param)...but in the javascript version : (param) ...Thanks guys.
Camilo Sanchez