个性化阅读
专注于IT技术分析

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);
    };
}
赞(0)
未经允许不得转载:srcmini » JavaScript闭包内循环简单实例

评论 抢沙发

评论前必须登录!