1

I searched but couldn't find an answer to this seemingly easy question, so...

Suppose I have a loop in which I need to set callbacks. My callback function looks like this:

function callback(var1) { // code }

Now my loop is something like this:

for( //condition)
{
  var x = something_different_each_time;
  document.getElementById('foo').addEventListener('click', function() { callback(x); }, false);
}

Now it looks like even if the loop runs n times, the anonymous function is compiled only once -- and hence every invocation of callback is called with the same argument (even though x varies in the loop every time).

I must be missing something here.. any help is greatly appreciated! :)

Anurag
  • 140,337
  • 36
  • 221
  • 257
ragebiswas
  • 3,818
  • 9
  • 38
  • 39
  • 1
    Similar questions: http://stackoverflow.com/questions/1734749/ http://stackoverflow.com/questions/643542/ http://stackoverflow.com/questions/1582634/ http://stackoverflow.com/questions/1331769/ http://stackoverflow.com/questions/1552941/ http://stackoverflow.com/questions/750486/ http://stackoverflow.com/questions/933343/ http://stackoverflow.com/questions/1579978/ http://stackoverflow.com/questions/1413916/ – Christian C. Salvadó May 21 '10 at 07:57
  • 1
    that is a comprehensive list @CMS. It would be nice to tag these questions with "closures" and "loops", so they can all be linked with a simple search. – Anurag May 21 '10 at 08:01
  • 1
    done, all questions are tagged "javascript", "closures", and "loops". Here's the link - http://stackoverflow.com/questions/tagged/javascript+closures+loops – Anurag May 21 '10 at 08:07
  • @Anurag, I just visited the SO homepage at the time you started to retag, and thought I had discovered some kind of clever, nefarious hacker as the front page was suddenly full of nearly identical questions by different posters. Then I noticed you were listed as the last active poster on each, and found my way here from the Activity tab on your profile. Joke's on me, but it sure looked fishy at first! – eyelidlessness May 21 '10 at 08:22
  • @CMS.. you're welcome. this is a very common issue, nice to have all answers consolidated! – Anurag May 21 '10 at 08:23
  • @eyelidlesness - how do you know I am not a machine? :P – Anurag May 21 '10 at 08:24
  • @Anurag, because if you are a machine then you're playing on my deep-seated fears of machines playing on my deep-seated fears, and I just can't cope with the idea of a machine existing with that much insight into my recursive fear of a machine like that existing. – eyelidlessness May 21 '10 at 08:27
  • @eyelidlessness - Good reason. Would you like to be a machine then I am playing on your deep seated fears of machines playing on your deep seated fears and you just can not cope with the idea of a machine existing with that much insight into your recursive fear of a machine like that existing? :D – Anurag May 21 '10 at 08:34
  • @Anurag @eyelidlessness Thanks, my head just imploded. (>_<) – deceze May 21 '10 at 08:37
  • @Anurag, you can't fool me! I was pretty convinced, until I realized that you swapped pronouns and expanded a contraction. Good show. – eyelidlessness May 21 '10 at 18:42

4 Answers4

2

The problem is that the block of the for statement doesn't creates a new scope, for that, the x variable belongs to its enclosing scope, and all anonymous functions refer to the same variable...

Use another function to create a new lexical environment to hold the value of x on each iteration:

for(/*condition*/) {
  var x = something_different_each_time;
  document.getElementById('foo').addEventListener('click', function () {
    return function(y) {
      callback(y);
    };
  }(x), false);
}
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • I think you're missing some braces around your anonymous function expression there. – deceze May 21 '10 at 08:05
  • 2
    @deceze: Nope, the parentheses are required only when the function is on a place where it can cause a grammatical ambiguity between a function expression and a function declaration, in this case, the function is in the arguments list of a function call, it's clearly on expression context, no ambiguity... e.g.: `(function (fn) { fn();})( function () {alert ('hi');} );` – Christian C. Salvadó May 21 '10 at 08:10
  • @deceze: maybe a better example: `var foo = function () { return 'bar'; }();` the function is invoked without problems... `foo === 'bar';` – Christian C. Salvadó May 21 '10 at 08:17
1

You should calculate x before calling your callback functin!

for( //condition)
{
  //var x = something_different_each_time;
  document.getElementById('foo').addEventListener('click', function() { 
   var x = something_different_each_time;
   callback(x); }, false);
}
Kunal
  • 362
  • 2
  • 8
0

Yes, the x will refer to the same variable in the enclosing scope, and since the function is executing later, it'll have the last value of x. Try this:

.addEventListener(
    'click',
    (function (i) {
        return function () { callback(i); }
    })(x),
    false
);

This creates a closure with the current value of x locked inside.

deceze
  • 510,633
  • 85
  • 743
  • 889