JavaScript 循环中的闭包:简单实用示例

JavaScript 循环中的闭包:简单实用示例

技术背景

在 JavaScript 中,循环内使用闭包时会遇到一些问题。这是因为在 JavaScript 里,变量作用域基于函数,而非像 C# 那样有块级作用域。在循环里创建的闭包会引用同一个变量,导致最终所有闭包都使用该变量的最终值。

实现步骤

ES6 解决方案:let

ES6 引入了 letconst 关键字,它们的作用域与 var 不同。在使用 let 作为循环索引时,每次循环都会创建一个新的变量 i,并具有块级作用域。示例代码如下:

1
2
3
4
5
for (let i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}

不过要注意,IE9 - IE11 和 Edge 14 之前的版本虽然支持 let,但在上述场景中处理有误,它们不会每次都创建新的 i,所有函数都会输出 3,就像使用 var 一样。Edge 14 及之后版本才正确处理。

ES5.1 解决方案:forEach

Array.prototype.forEach 函数在 2015 年已广泛可用。当主要对数组值进行迭代时,.forEach() 能为每次迭代提供清晰、自然的方式来创建不同的闭包。示例如下:

1
2
3
4
5
6
7
8
var someArray = [ /* whatever */ ];
// ...
someArray.forEach(function(arrayElement) {
// ... code code code for this one element
someAsynchronousFunction(arrayElement, function() {
arrayElement.doSomething();
});
});

如果使用 jQuery,$.each() 函数也有类似功能。

经典解决方案:闭包

可以将每个函数内的变量绑定到函数外部一个不变的值。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var funcs = [];

function createfunc(i) {
return function() {
console.log("My value: " + i);
};
}

for (var i = 0; i < 3; i++) {
funcs[i] = createfunc(i);
}

for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}

也可以使用立即执行函数表达式(IIFE):

1
2
3
4
5
6
7
8
9
10
11
12
13
var funcs = [];

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

for (var j = 0; j < 3; j++) {
funcs[j]();
}

使用 Function.prototype.bind

1
2
3
4
5
6
7
8
9
var funcs = {};
for (var i = 0; i < 3; i++) {
funcs[i] = function(x) {
console.log('My value: ' + x);
}.bind(this, i);
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}

为了提高性能,可以先在循环外创建函数,再在循环内绑定结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
function log(x) {
console.log('My value: ' + x);
}

var funcs = [];

for (var i = 0; i < 3; i++) {
funcs[i] = log.bind(this, i);
}

for (var j = 0; j < 3; j++) {
funcs[j]();
}

最佳实践

  • 若项目支持 ES6,优先使用 let 关键字,代码简洁且易于理解。
  • 当处理数组迭代时,可使用 forEach 方法,避免手动管理索引变量。
  • 在不支持 ES6 的环境中,使用闭包或 Function.prototype.bind 来解决问题。

常见问题

所有闭包都输出相同值

这是因为闭包引用了同一个变量。可以使用上述解决方案,如 let、闭包、forEachFunction.prototype.bind 来解决。

浏览器兼容性问题

部分旧浏览器(如 IE9 - IE11 和 Edge 14 之前的版本)对 let 支持有问题。在需要兼容这些浏览器时,可采用 ES5 的解决方案。


JavaScript 循环中的闭包:简单实用示例
https://119291.xyz/posts/2025-05-09.javascript-closure-inside-loops-example/
作者
ww
发布于
2025年5月9日
许可协议