带有 for 循环的 Javascript 闭包

标签 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

最佳答案

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

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 中,变量是函数的本地变量,而不是 {...} block 的本地变量(例如在 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 值)。

编辑

在当前的 Javascript 中,上述所有内容都适用于 var 变量(函数作用域),但您也可以使用 let block 作用域变量。

这意味着现在可以简化代码,例如:

lambdas = [];
for (let i=0; i<10; i++) {
    let j = i*i; // Variable j is block scoped
    lambdas.push(function(){ return j; });
}
console.log(lambdas[3]()); // ==> output 9
console.log(lambdas[5]()); // ==> output 25

(使用 var j = i*i; 相反,两者都会得到 81)

关于带有 for 循环的 Javascript 闭包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26687859/

相关文章:

javascript - 如何在 Html/Css 和 React js 中实现这个 slider ?

javascript - 如何在 jQuery 单击事件中访问 HTML 元素的属性

javascript - 如何使用闭包在循环内设置事件监听器

Javascript Typedef 使用参数时出错

javascript - 为什么 ajax 调用中的闭包可以访问外部范围?

javascript - CSS - 使用悬停效果时无法将我的图像居中。

javascript - 客户支持字幕滚动

javascript 字符串作为命令行,为什么不使用 eval()?

javascript - 此示例中存在什么类型的继承(原型(prototype)除外)

python - 判断函数是否是嵌套函数