假设我们有一个简单对象的列表:
var things = [
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
{ id: 3, name: 'three' }
];我们需要迭代这些对象,并将它们注册为一些后续事件的参数。朴素的方法在所有回调之间共享相同的对象引用,因此每个回调都针对最后一个项触发:
for (var i = 0; i < things.length; i++) {
var o = things[i];
setTimeout(function() { doSomethingWith(o); }, i * 1000);
}一个典型的解决方案是创建一个闭包,限制对象引用的范围:
for (var i = 0; i < things.length; i++) {
(function() {
var o = things[i];
setTimeout(function() { doSomethingWith(o); }, i * 1000);
})();
}如果我们不是针对IE<9,我们可以依靠.forEach()
things.forEach(function(o, i) {
var o = things[i];
setTimeout(function() { doSomethingWith(o); }, i * 1000);
});但是,我们最终还是在将匿名函数传递给forEach()时创建了一种闭包。
是否有一种在没有闭包或函数引用的情况下完成此操作的方法?
根本的问题有三个方面:
setTimeout()中的函数引用(或任何可能的)--使您(我)感觉您正在创建一个闭包。所以,我倾向于忘记外部封闭。.forEach()在IE>9中解决,除非应用程序或组织风格指南为闭包指定换行符+缩进。也许更好的方法是:,在我们开始强制创建闭包之前,我们都做了什么?
发布于 2015-02-24 16:55:42
我不认为在这里使用闭包有什么问题。它们是javascript中的自然工具,对于具有本地状态的异步回调非常必要--因为我们希望避免全局状态。
如果您非常关心缩进,可以将作用域提供的IEFE与循环放在同一行上:
for (var i = 0; i < things.length; i++) (function() {
var o = things[i];
setTimeout(function() { doSomethingWith(o); }, i * 1000);
}());否则,您已经很好地使用了forEach。注意,您不需要在代码中关心IE<=8,因为如果您想支持forEach,那么它是微不足道的。
当然,ES6将为我们提供一个用新语法解决这个very common problem的let语句--您需要使用一个6到5的转换程序:
for (let i = 0; i < things.length; i++) {
// ^^^
var o = things[i];
setTimeout(function() { doSomethingWith(o); }, i * 1000);
}如果您想要一个非常清晰的代码组织,请将闭包显式化:
function makeDoer(thing) {
return function() { doSomethingWith(thing); };
}
for (var i = 0; i < things.length; i++) {
setTimeout(makeDoer(things[i]), i*1000);
}在我们开始强制创建闭包之前,我们都做了什么?
我们使用全球状态,并以不同的方式解决我们的问题。例如,您的情况可以通过半递归函数来处理,这样做要好得多:
var i = 0;
function next() {
if (i < things.length) {
doSomethingWith(things[i++]);
setTimeout(next, 1000);
}
}
next();发布于 2015-02-24 18:16:57
我想出了两种不同的方法。第一个方法是将参数绑定到该方法的调用。它在函数中克隆参数thingsi并使用它作为参数。
for (var i = 0; i < things.length; i++) {
var o = things[i];
setTimeout(doSomethingWith.bind(null, things[i]), i * 1000);
} 第二种方式,
setTimeout在ms中的参数时间之后,接受您将调用的函数中的参数,它在定义参数时也会复制值,因此变量值可以在之后更改,setTimeout将保证将正确的值作为参数传递。
for (var i = 0; i < things.length; i++) {
var o = things[i];
setTimeout(function(param) { doSomethingWith(param); }, i * 1000, o);
}希望能帮上忙!
https://stackoverflow.com/questions/28701290
复制相似问题