JavaScript闭包内循环简单实例

var funcs = [];

for (var i = 0; i < 3; i++) {

funcs[i] = function() {

console.log(i);

};

}

for (var j = 0; j < 3; j++) {

funcs[j]();

}

如上,输出的结果是3个“3”,但是想输出的是“0 1 2”。

当使用时间监听器导致函数运行延迟时,也出现同样的问题:

var buttons = document.getElementsByTagName("button");

for (var i = 0; i < buttons.length; i++) {

buttons[i].addEventListener("click", function() {

console.log(i);

});

}

或异步代码:

const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));

for(var i = 0; i < 3; i++){

wait(i * 100).then(() => console.log(i));

}

这个问题的解决办法是什么?

问题在于变量i,在每个匿名函数中,都被绑定到函数外的同一个变量上。

经典解决方案:闭包

你要做的就是将每个函数中的变量绑定到函数外部一个单独的,不变的值:

var funcs = [];

function createfunc(i) {

    return function() { console.log(i); };

}

for (var i = 0; i < 3; i++) {

    funcs[i] = createfunc(i);

}

for (var j = 0; j < 3; j++) {

    funcs[j]();

}

由于JavaScript中没有块作用域,只有函数作用域,通过将函数创建在一个新函数中,你可以确保i的值保持为你想要的值。

JS2015解决方案:forEach

随着相对广泛的可用性数组,原型,值得注意的是,在那些设计值数组迭代的情况下,forEach提供了一种干净、自然的方法来每个迭代获得不同的闭包。也就说,假设你有某种包含值的数组(DOM引用,对象等),并且出现了针对每个元素设置回调的问题,你可以这样做:

var arrays = [ /*values*/ ];

arrays.forEach(function(arrayElement) {

someAsynchronousFunction(arrayElement, function() {

arrayElement.doSomething();

});

});

其思想是,forEach循环使用的回调函数的每次调用都是它自己的闭包,传入该处理程序的参数是特定于迭代的特定步骤的数组元素。如果在异步回调中使用它,它将不会与在迭代的其他步骤中建立的任何其他回调发生冲突。如果你碰巧使用jQuery, $.each()函数提供了类似的功能。

ES6解决方案:let

ECMAScript

6 (ES6)引入了新的let和const关键字,它们的作用域不同于基于var的变量。例如,在一个基于let的索引的循环中,循环中的每个迭代都有一个新值i,其中每个值都在循环的作用域内,因此你的代码将如你所期望的那样工作。

for (let i = 0; i < 3; i++) {

funcs[i] = function() {

console.log(i);

};

}

以上是 JavaScript闭包内循环简单实例 的全部内容, 来源链接: www.h5w3.com/199982.html

回到顶部