简体   繁体   中英

How to get outer loop index inside anonymous function call?

I have the following javascript code :

var Person = [['John', 0, 0, 0],['Chris', 1, 0, 0]];
for (i = 0; i < Person.length; i++ )
{
    someObj.myMethod(Person[i][0], function (object) {
        console.log(i); //this prints 2, I want 0 and 1 as per the loop

        //here I want to access other members of Person[i] array, like Person[i][1], Person[i][2] and Person[i][3]
        //but these console.log() print 'undefined' because i = 2 !!
        console.log(Person[i][1]);
        console.log(Person[i][2]);
        console.log(Person[i][3]);
    });
}

Inside the anonymous function called inside my myMethod(), value of i is '2'. Please suggest how to get i = 0 in first cycle of for loop and then 1 in the second loop.

With a closure, this a solution:

var Person = [['John', 0, 0, 0],['Chris', 1, 0, 0]];
for (x = 0; x < Person.length; x++ )
{
    (function(i){   //We add a closure
        someObj.myMethod(Person[i][0], function (object) {
          console.log(i);
          console.log(Person[i][1]);
          console.log(Person[i][2]);
          console.log(Person[i][3]);
      });
    })(x);      //Call the closure function with the value of the counter
}

I changed the original counter to x to make it more understandable (so you dont confuse that variable with the original i ), but if it keeps named also i , it will work, too.

This way, each loop cycle has it's own variable x ( not shared ), so it doesn't get overwritten by the for loop, which was causing the problem ( i was shared) :)

Cheers

You have a leaky closure . Try this:

var Person = [['John', 0, 0, 0],['Chris', 1, 0, 0]];
for (i = 0; i < Person.length; i++ )
{
    doIt(i);
}

function doIt(i) {
    someObj.myMethod(Person[i][0], function (object) {
        console.log(i); //this prints 2, I want 0 and 1 as per the loop

        //here I want to access other members of Person[i] array, like Person[i][1], Person[i][2] and Person[i][3]
        //but these console.log() print 'undefined' because i = 2 !!
        console.log(Person[i][1]);
        console.log(Person[i][2]);
        console.log(Person[i][3]);
    });
}

Basically, your original inner anonymous function is being passed elsewhere to be executed later, by which stage the i variable has already been incremented to 2 in the for loop (they're effectively referencing the one copy of i ). Javascript is function scoped, so introducing a new function with it's own parameter to trap the specific value of i , you can decouple your anonymous function from the shared counter in the for loop.

Note that you can also make the function ( doIt in my example) an immediately executing anonymous function like Edgar has , which means that no one else can call the logically private closure function (though it arguably makes it a little harder to read to someone not used to javascript closures).

It depends entirely on when the callbacks are executed. If they are executed immediately , as the loop is running, then the values will be as you expect. The following outputs 0 and 1 :

var someObj = {
    myMethod: function(person, callback) {
        callback();
    }
}

var Person = [['John', 0, 0, 0],['Chris', 1, 0, 0]];
for (i = 0; i < Person.length; i++ )
{
    someObj.myMethod(Person[i][0], function (object) {
        console.log(i); 
    });
}

However, if the callbacks are stored and executed later, then (and this is the key thing to understand) each callback stored has closed over the same variable i and that variable's value is 2 after the loop has completed.

In other words, the closure is over the variable itself, not its value at the time the closure is created . As others have stated, this problem is easily solved using a wrapper function that receives i as an argument. This creates a new variable for each callback to close over.

Here's a contrived example:

var someObj = {

    callbacks: [],

    myMethod: function(person, callback) {
        someObj.callbacks.push(callback);
    }
}

var Person = [['John', 0, 0, 0],['Chris', 1, 0, 0]];
for (i = 0; i < Person.length; i++ )
{
    (function(i) {
        someObj.myMethod(Person[i][0], function (object) {
            console.log(i); 
        });
    })(i);
}

someObj.callbacks[0]();
someObj.callbacks[1]();

Another option in your case is to pass the elements of Person to your callback, instead of their index in the array.

Another way to deal with closure issue that you have is to create local variable and pass it to your function.

var Person = [['John', 0, 0, 0],['Chris', 1, 0, 0]];

for (var i = 0; i < Person.length; i++ ) {
    var x = i;

    someObj.myMethod(Person[x][0], function (object) {
        console.log(Person[x][1]);
        console.log(Person[x][2]);
        console.log(Person[x][3]);
    });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM