如何在匿名函数调用中获取外部循环索引?

时间:2014-01-09 03:08:08

标签: javascript anonymous-function

我有以下javascript代码:

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]);
    });
}

在我的myMethod()里面调用的匿名函数里面,i的值是'2'。请建议如何在for循环的第一个循环中获得i = 0,然后在第二个循环中获得1。

4 个答案:

答案 0 :(得分:3)

使用闭包,这是一个解决方案:

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
}

我将原始计数器更改为x以使其更易于理解(因此您不要将该变量与原始i混淆),但如果它仍然命名为i,它将会工作也是。

这样,每个循环周期都有自己的变量x not shared ),因此它不会被for循环覆盖,导致问题({{ 1}}共享):)

干杯

答案 1 :(得分:2)

你有一个漏洞closure。试试这个:

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]);
    });
}

基本上,你原来的内部匿名函数正被传递到其他地方以便稍后执行,在这个阶段,i变量在for循环中已经增加到2(它们实际上是在引用一份i)。 Javascript是函数作用域,所以引入一个新函数和它自己的参数来捕获i的特定值,你可以将你的匿名函数与for循环中的共享计数器分离。

请注意,您还可以使函数(在我的示例中为doIt)立即执行匿名函数,如Edgar has,这意味着没有其他人可以调用逻辑上的私有闭包函数(尽管它可以说是让那些不熟悉javascript闭包的人阅读有点困难。)

答案 2 :(得分:2)

执行回调时完全 如果它们立即执行 ,则在循环运行时,值将如您所愿。以下输出01

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); 
    });
}

但是,如果回调是存储并稍后执行,那么(这是理解的关键)每个存储的回调都关闭了相同的变量{{循环完成后,该变量的值为i

换句话说,闭包在变量本身上,而不是在创建闭包时的值。正如其他人所说,使用接收2作为参数的包装函数可以轻松解决此问题。这会为每个要关闭的回调创建一个 new 变量。

这是一个人为的例子:

i

你的另一个选择是将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](); 的元素传递给你的回调,而不是它们在数组中的索引。

答案 3 :(得分:1)

处理闭包问题的另一种方法是创建局部变量并将其传递给函数。

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]);
    });
}