【问题标题】:Closures in a for loop and lexical environmentfor循环和词法环境中的闭包
【发布时间】:2010-12-11 18:46:45
【问题描述】:

简单案例:我想加载几个具有通用名称和后缀的图像,例如:image0.png、image1.png、image2.png ... imageN.png

我正在使用一个简单的 for 循环:

var images = [];
for (var i=1; i<N; i++) {
    images[i] = new Image();
    images[i].onload = function () {
        console.log("Image " + i + " loaded");
    };
    images[i].src = "image" + i + ".png";
}

我在控制台中得到的是:

Image N loaded
Image N loaded
Image N loaded
...
Image N loaded

但我想要的应该是这样的:

Image 0 loaded
Image 1 loaded
Image 2 loaded
...
Image N loaded

为什么会这样? 如何获得我想要的行为?

【问题讨论】:

标签: javascript closures


【解决方案1】:

函数内的i 在函数被执行 时进行评估,而不是在您将其分配给onload 时进行评估。当您的任何onload 函数触发时,您的 for 循环已经完成,因此它们都看到了最终值 N

要捕获i 的当前值,您需要将其作为参数传递给另一个函数,在该函数中可以将其捕获为局部变量:

function captureI(i) {
    return function () {
        console.log("Image " + i + " loaded");
    };
}

var images = [];
for (var i=1; i<N; i++) {
    images[i] = new Image();
    images[i].onload = captureI(i);
    images[i].src = "image" + i + ".png";
}

这是因为每次调用captureI 时,都会为captureI 的该实例创建一个新的局部变量。本质上,您正在创建 N 不同的变量,每个 onload 函数捕获该变量的不同实例。

【讨论】:

  • 感谢最后的解释。
【解决方案2】:

您可以将其包装在一个闭包中以避免使用i 变量,这是一个循环变量,因此会发生变化:

(function(j) {
  images[i].onload = function () {
      console.log("Image " + i + ", " + j + " loaded");
  };
})(i);

这演示了ij 之间的区别,i 是一个循环变量,会发生变化,j 是一个函数绑定参数,不会发生变化。

在此处查看 jsfiddle:

【讨论】:

    【解决方案3】:

    您的循环计数器变量已被覆盖。查看this 常见问题解答条目,详细解释它发生的原因以及如何解决该问题。

    【讨论】:

      【解决方案4】:

      由于变量i 是在循环范围之外声明的,因此在循环完成后它会保留其最终值。然后你创建的匿名函数都绑定到这个变量,当它们被调用时,它们都得到相同的最终值N

      this question 对此进行了很好的讨论。

      【讨论】:

      • 这将为所有图像提供 N-1 :-)
      猜你喜欢
      • 1970-01-01
      • 2018-05-02
      • 2012-12-15
      • 2011-01-12
      • 2021-02-20
      • 2021-10-27
      • 2014-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多