1

我对 javascript 的特定行为有疑问:

我有一个对象,我想用生成的函数填充它。每个函数都包含一个在函数生成循环期间更改的变量。

我的问题是在将函数分配给对象时变量没有被替换。相反,对变量的引用保留在函数中,并且在执行函数时只记住变量的最后一个值。

这是一个最小的例子(也在 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]();   
}

第二个循环执行所有生成的函数并打印一个 9 作为结果。但我希望他们打印变量在生成时的值(0、1、2、...)。

有没有办法做到这一点?提前致谢。

4

4 回答 4

1

问题是您创建的所有函数都共享对同一nr变量的引用。当您调用它们时,它们使用该引用获取值,因此它们都得到相同的结果。

像这样解决它:

for (var i = 0; i < 10; i++){
    (function(nr) {
        obj[nr] = function(){
            console.log("result: " + nr);
        }
    })(i);
}
于 2013-07-06T20:12:29.130 回答
1

一种方法是调用返回函数的函数:

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

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

另一种方法是立即调用函数表达式

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

在后一种情况下,(function(i) ... )(i)导致i作为参数传递到内部函数范围内的外部函数的永久绑定

于 2013-07-06T20:12:32.407 回答
1

您的猜测是正确的,是的,有一个解决方案:

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

与其他一些块结构语言不同,JavaScript 变量的范围是函数级别。也就是说,您在for循环内声明“nr”这一事实并不会使其成为该块的“本地”——效果与您在函数顶部声明它的效果完全相同。

通过使用该匿名函数引入另一个函数范围,您可以创建“nr”值的新副本,然后返回的实际函数可以私有访问该值。这些函数中的每一个都将拥有它自己的“nr”值的副本,就像初始化“obj”数组的那个槽时一样。

于 2013-07-06T20:12:38.300 回答
0

你想要的是为你创建的每个函数创建一个闭包。
然而, var(s) 没有块范围,所以你的代码是一样的:

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

这希望使所有函数都将引用相同的“nr”变量更加明显。

您想要做的意味着每次都创建一个新范围,这可以使用 bind 来完成,但是让我们坚持您的初衷并每次使用 lambda 构建一个新的闭包:

var obj = {};
for (var i = 0; i < 10; i++) {
   obj[i] = (function(){
                          var this_nr = i;
                          return function(){
                                console.log("result: " + this_nr);
                          }
                        }() );
}
于 2013-07-06T20:17:35.607 回答