生成的函数保留对变量的引用,而不是值替换

Generated functions keep reference to variable instead of value replacement?

本文关键字:替换 变量 函数 保留 引用      更新时间:2023-09-26

我对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、...(。

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

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

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作为参数传递给内部函数范围内的外部函数的永久绑定

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

像这样解决它:

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

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

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

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

通过使用该匿名函数引入另一个函数作用域,可以创建"nr"值的新副本,然后返回的实际函数可以私下访问该副本。 这些函数中的每一个都有自己的"nr"值副本,就像初始化"obj"数组的插槽时一样。

你想要的是

为你创建的每个函数创建一个闭包。
然而,var 没有块作用域,所以你的代码与 :

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

这有望使所有函数都更明显地引用相同的"nr"var。

您要做的是每次创建一个新作用域,这可以使用绑定来完成,但是让我们坚持您的原始意图,每次都使用 lambda 构建一个新的闭包:

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