views:

88

answers:

2

Possible Duplicate:
Javascript closure inside loops - simple practical example

Rather than explaining the question, I'll give an example:

for (var i = 0; i < 100; i ++) {
  get_node(i).onclick = function() {
    do_something_very_important(i);
  }
}

Is there any way to have the value of i substituted into the function upon creation rather than execution? Thanks.

+1  A: 

Yes, you can, but that won't work for the example you provided. You would be having a very common closure problem in that for loop.

Variables enclosed in a closure share the same single environment, so by the time the onclick callback is called, the for loop will have run its course, and the i variable will be left pointing to the last value it was assigned. In your example, the do_something_very_important() function will be passed the value 100 for each node, which is not what you intend.

You can solve this problem with even more closures, using a function factory:

function makeClickHandler(i) {  
  return function() {  
    do_something_very_important(i);
  };  
}

// ...

for(var i = 0; i < 100; i++) {
  get_node(i).onclick = makeClickHandler(i);
}

This can be quite a tricky topic, if you are not familiar with how closures work. You may want to check out the following Mozilla article for a brief introduction:


UPDATE:

You could also inline the above function factory as @adamse suggested in the other answer. This is actually a more common approach, but is practically the same as the above:

for(var i = 0; i < 100; i++) {
  get_node(i).onclick = (function(p) {
    return function () {
      // we could have used i as a parameter variable as well,
      // but we're using p to better illustrate what's happening
      do_something_very_important(p); 
    }
  })(i);
}

Any yet another solution is to enclose each iteration in its own scope, by using self invoking anonymous functions:

for(var i = 0; i < 100; i++) {
  (function (p) {
    // we now have a separate closure environment for each
    // iteration of the loop
    get_node(i).onclick = function() {
      do_something_very_important(p);
    }
  })(i);
}
Daniel Vassallo
Thanks for the link, that was helpful.
Jesse
A: 

Yes this works...

for (var i = 0; i < 100; i++) {
  get_node(i).onclick = (function(i) {
    return function () {
      do_something_very_important(i);
    }
  })(i);
}
adamse
Thanks, that's exactly what I was looking for.
Jesse