如何解决循环内的 JavaScript 闭包——简单实用的例子
好吧,问题是i
每个匿名函数内的变量都绑定到函数外的同一个变量。
ES6解决方案: let
ECMAScript 6 (ES6) 引入了范围不同于基于变量的新关键字let
和const
关键字var
。例如,在具有let
基于索引的循环中,循环中的每次迭代都会有一个i
具有循环范围的新变量,因此您的代码将按预期工作。
for (let i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
但是请注意,Edge 14 之前的 IE9-IE11 和 Edge 支持let
但会出现上述错误(它们不会i
每次都创建一个新的,因此上面的所有函数都会像我们使用 那样记录 3 var
)。Edge 14 终于做到了。
ES5.1 解决方案:forEach
随着该Array.prototype.forEach
函数的相对广泛的可用性(2015 年),值得注意的是,在主要涉及值数组迭代的情况下,.forEach()
提供了一种干净、自然的方法来为每次迭代获得不同的闭包。也就是说,假设您有某种包含值(DOM 引用、对象等)的数组,并且出现设置特定于每个元素的回调的问题,您可以这样做:
var someArray = [ /* whatever */ ];
// ...
someArray.forEach(function(arrayElement) {
// ... code code code for this one element
someAsynchronousFunction(arrayElement, function() {
arrayElement.doSomething();
});
});
这个想法是与.forEach
循环一起使用的回调函数的每次调用都将是它自己的闭包。传递给该处理程序的参数是特定于迭代特定步骤的数组元素。如果在异步回调中使用它,它不会与迭代的其他步骤中建立的任何其他回调发生冲突。
如果您碰巧使用 jQuery,该$.each()
函数为您提供了类似的功能。
经典解决方案:闭包
您想要做的是将每个函数内的变量绑定到函数外的一个单独的、不变的值:
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]();
}
Run code snippetHide results
Full page
由于 JavaScript 中没有块作用域——只有函数作用域——通过将函数创建包装在一个新函数中,您可以确保“i”的值保持如您所愿。
解决方法
var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
// and store them in funcs
funcs[i] = function() {
// each should log its value.
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}
它输出这个:
My value: 3
My value: 3
My value: 3
而我希望它输出:
My value: 0
My value: 1
My value: 2
当函数运行延迟是由于使用事件监听器导致时,也会出现同样的问题:
var buttons = document.getElementsByTagName("button");
// let's create 3 functions
for (var i = 0; i < buttons.length; i++) {
// as event listeners
buttons[i].addEventListener("click",function() {
// each should log its value.
console.log("My value: " + i);
});
}
<button>0</button>
<br />
<button>1</button>
<br />
<button>2</button>
… 或异步代码,例如使用 Promises:
// Some async wait function
const wait = (ms) => new Promise((resolve,reject) => setTimeout(resolve,ms));
for (var i = 0; i < 3; i++) {
// Log `i` as soon as each promise resolves.
wait(i * 100).then(() => console.log(i));
}
在for in
和for of
循环中也很明显:
const arr = [1,2,3];
const fns = [];
for(var i in arr){
fns.push(() => console.log(`index: ${i}`));
}
for(var v of arr){
fns.push(() => console.log(`value: ${v}`));
}
for(var f of fns){
f();
}
这个基本问题的解决方案是什么?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。