1

I have a question to a specific behavior of javascript:

I have an object which I want to fill with generated functions. Each function contains a variable which is changed during the loop of function generation.

My problem is that the variable does not get replaced when assigning the function to the object. Instead the reference to the variable stays in the function and when executing the function only the last value of the variable is remembered.

Here is a minimal example (also on jsfiddle: http://jsfiddle.net/2FN6K/):

var obj = {};
for (var i = 0; i < 10; i++){
    var nr = i;
    obj[i] = function(){
        console.log("result: " + nr);
    }
}

for (var x = 0; x < 10; x++){
    obj[x]();   
}

The second loop executes all generated functions and all print a 9 as result. But i want that they print the value which the variable had at the time of generation (0, 1, 2, ...).

Is there a way to do this? Thanks in advance.

DerDree
  • 277
  • 4
  • 12
  • http://stackoverflow.com/questions/8214753/access-a-copied-integer-variable-in-javascript-anonymous-method – luk2302 Jul 06 '13 at 20:12
  • exact duplicate of [Javascript closure inside loops - simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Bergi Jul 06 '13 at 20:13

4 Answers4

1

The problem is that all the functions you create are sharing a reference to the same nr variable. When you call them they fetch the value using that reference and therefore all of them get the same result.

Solve it like this:

for (var i = 0; i < 10; i++){
    (function(nr) {
        obj[nr] = function(){
            console.log("result: " + nr);
        }
    })(i);
}
Jon
  • 428,835
  • 81
  • 738
  • 806
1

One approach is to call a function that returns a function:

function makeFunc(i) {
    return function() {
        console.log("result: " + i);
    }
}

for (...) {
    obj[i] = makeFunc(i);
}

Another approach is the immediately invoked function expression:

for (i = 0; ...; ...) {
    (function(i) {
        obj[i] = function() {
           console.log("result: " + i);
        }
    })(i);
}

where in the latter case the (function(i) ... )(i) results in a permanent binding of i passed as a parameter to the outer function within the scope of the inner function

Alnitak
  • 334,560
  • 70
  • 407
  • 495
1

Your surmise is correct, and yes, there's a solution:

  obj[i] = function( nr_copy ) {
    return function() {
      console.log("result: " + nr_copy);
    };
  }( nr );

JavaScript variables are scoped at the function level, unlike some other block-structured languages. That is, the fact that you declare "nr" inside the for loop doesn't make it "local" to that block — the effect is precisely the same as if you'd declared it at the top of the function.

By introducing another function scope with that anonymous function, you make a new copy of the value of "nr", which is then privately accessible to the actual function that's returned. Each of those functions will have it's own copy of the value of "nr" as it stood when that slot of the "obj" array was initialized.

Pointy
  • 405,095
  • 59
  • 585
  • 614
0

what you want is to create a closure for every function you create.
Yet, the var(s) have not a block scope, so your code is the same as :

var obj = {};
var i;
var nr;
for (i = 0; i < 10; i++){
    nr = i;
    obj[i] = function(){
         console.log("result: " + nr);
     }
 }

which hopefully makes it more obvious all functions will refer to the very same 'nr' var.

What you want to do implies creating a new scope each time, which might be done using bind, but let's stick to your original intent and build a new closure each time with a lambda :

var obj = {};
for (var i = 0; i < 10; i++) {
   obj[i] = (function(){
                          var this_nr = i;
                          return function(){
                                console.log("result: " + this_nr);
                          }
                        }() );
}
GameAlchemist
  • 18,995
  • 7
  • 36
  • 59