生成的函数是否继续引用变量而不是值替换?

时间:2013-07-06 20:07:33

标签: javascript variables

我对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 个答案:

答案 0 :(得分:1)

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

像这样解决:

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

答案 1 :(得分: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的永久绑定作为参数传递给内部函数范围内的外部函数

答案 2 :(得分:1)

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

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

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

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

答案 3 :(得分: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'var。

你想要做的事情意味着每次创建一个新的范围,这可能是使用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);
                          }
                        }() );
}