for循环和词汇环境中的闭包

时间:2010-12-11 18:46:45

标签: javascript closures

简单案例:我想加载几个具有通用名称和后缀的图像,例如: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

为什么会这样? 我怎样才能达到我想要的行为?

4 个答案:

答案 0 :(得分:3)

当函数执行时,将评估函数内的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函数捕获变量的不同实例。

答案 1 :(得分:2)

你可以将它包装在一个闭包中,以避免使用i变量,这是一个循环变量,从而改变:

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

这证明了i(一个循环变量和更改)与j(一个函数绑定参数)之间的区别,它不会改变。

请参阅此处的jsfiddle:

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

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