gpt4 book ai didi

循环内的 JavaScript 闭包 – 简单的实际示例

转载 作者:行者123 更新时间:2023-11-28 06:48:34 24 4
gpt4 key购买 nike

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>

...或异步代码,例如使用 promise :

// 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 infor 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 (const n of arr) {
var obj = { number: n }; // or new MyLibObject({ ... })
fns.push(() => console.log("n:", n, "|", "obj:", JSON.stringify(obj)));
}

for(var f of fns){
f();
}

这个基本问题的解决方案是什么?

最佳答案

问题是每个匿名函数中的变量 i 都绑定(bind)到函数外部的同一个变量。

ES6解决方案:let

ECMAScript 6 (ES6) 引入了新的 letconst 关键字,它们的作用域与基于 var 的变量不同。例如,在具有基于 let 的索引的循环中,循环中的每次迭代都会有一个具有循环范围的新变量 i,因此您的代码将按您的预期工作。资源有很多,但我推荐2ality's block-scoping post作为一个重要的信息来源。

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

但请注意,IE9-IE11 和 Edge 14 之前的 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() 函数可为您提供类似的功能。

<小时/>

经典解决方案:闭包

您想要做的是将每个函数内的变量绑定(bind)到函数外部一个单独的、不变的值:

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]();
}

由于 JavaScript 中没有 block 作用域 - 只有函数作用域 - 通过将函数创建包装在新函数中,您可以确保“i”的值保持您想要的样子。

关于循环内的 JavaScript 闭包 – 简单的实际示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33178171/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com