用于循环的Javascript闭包

时间:2014-11-01 09:28:57

标签: javascript closures

我做了一些研究后放弃了这个。我的问题如下:

post,我了解以下代码。

var funcs = {};
for (var i = 0; i < 3; i++) {          // let's create 3 functions
funcs[i] = function() {            // and store them in funcs
    var item = "item" + i;         // inside
    console.log("item: " + item + ", i: " + i); // each should log its value.
    };
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

Result:
item: item3, i: 3
item: item3, i: 3
item: item3, i: 3

但如果我在function()之外更改移动var item = "item" + i,结果将会有所不同。这是为什么?

var funcs = {};
for (var i = 0; i < 3; i++) {          // let's create 3 functions
    var item = "item" + i;             // outside
    funcs[i] = function() {            // and store them in funcs
        console.log("item: " + item + ", i: " + i); // each should log its value.
    };
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

Result:
item: item2, i: 3
item: item2, i: 3
item: item2, i: 3

2 个答案:

答案 0 :(得分:1)

您面临的关键问题是闭包是通过引用捕获变量,而不是通过值捕获变量。例如:

var i = 0;
var f = function(){ i++; console.log(i); }
var g = function(){ i++; console.log(i); }
f(); g(); f(); g();

这将显示1,2,3,4,因为fg没有捕获变量i的值,但变量本身因此都在变异变量

一个非常常见的错误是在循环中创建多个闭包,忘记所有闭包都将捕获相同的变量(循环中使用的变量),因此当此循环变量递增时已经创建的闭包会看到它的变化。例如,在你的代码中

for (var i = 0; i < 3; i++) {
    funcs[i] = function() {
        var item = "item" + i; // inside
        console.log("item: " + item + ", i: " + i);
    };
}

item是函数的局部变量,但i来自外部,它在所有函数之间共享。例如,可以通过

看到这一点
i = 42;
funcs[0](); funcs[1](); funcs[2](); // Displays 42 three times
i = 99;
funcs[0](); funcs[1](); funcs[2](); // Now they all display 99

在你的问题的第二个案例中,事情略有不同,因为

for (var i = 0; i < 3; i++) {
    var item = "item" + i;
    funcs[i] = function() {
        console.log("item: " + item + ", i: " + i);
    };
}

item变量现在也已共享,因为它在函数之外定义,而捕获。在显示中,您会看到分配给它的最后一个值。

需要记住的是,在Javascript中,变量是函数的本地,而不是{...}块的本地(例如,在C ++中),因此区别因素是如果变量是在函数内部或函数外部声明的,那么它们是在循环内部或外部声明的。

要克服这个常见问题并为循环中创建的每个闭包创建一个单独的索引,您通常会看到类似

的代码
var f = (function(i){return function(){ ... };})(i);

创建函数并立即调用以返回另一个函数。

原因是创建单独的新变量的唯一方法是函数,让我们创建一个返回函数的函数模式:有用的闭包(内部) case将捕获每个迭代不同的变量(因为它是外部参数)。

将代码更改为

for (var i = 0; i < 3; i++) {
    funcs[i] = (function(i){return function() {
        var item = "item" + i;
        console.log("item: " + item + ", i: " + i);
    };})(i);
}

将按预期工作(即每个函数现在都有自己的i值。)

答案 1 :(得分:0)

在第一种情况下,item变量在函数运行之前不存在,此时它对每个函数都是局部的并且设置为等于"item" + ii3,因为它在外部范围内声明,3是循环结束时的内容。

在第二种情况下,item变量存在于函数&#39;包含范围 - 与i的范围相同 - 并且在第一个循环的最后一次迭代期间它已设置为"item2"。在该循环i内部的值为012 - 当循环结束时,它只被设置为3