假设我们有一个简单对象列表:
var things = [
{ id: 1, name: 'one' },
{ id: 2, name: 'two' },
{ id: 3, name: 'three' }
];
我们需要迭代这些对象并将它们注册为稍后事件的参数。天真的方法在所有回调之间共享相同的对象引用,因此每个回调都触发 last 项:
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&lt; 9,我们可以依赖.forEach()
:
things.forEach(function(o, i) {
var o = things[i];
setTimeout(function() { doSomethingWith(o); }, i * 1000);
});
但是,我们最终在这种情况下创建了一种关闭,无论如何将匿名函数传递给forEach()
。
有没有办法在没有闭包或函数引用的情况下完成此操作?
潜在的问题是三方面的:
setTimeout()
的函数引用(或其可能的任何内容)会让您(我)感觉就像您正在创建关闭。所以,我倾向于忘记外封闭。.forEach()
&gt; 9解决,除非应用程序或组织样式指南指示换行符+缩进闭合。也许更好的方式就是这样:在我们开始强制创建闭包之前,我们都做了什么恶魔?
答案 0 :(得分:1)
我认为在这里使用闭包没有任何问题。它们是javascript中的自然工具,对于具有本地状态的异步回调非常必要 - 因为我们希望避免全局状态。
如果您非常关心缩进,可以将提供范围的IEFE放在与循环相同的行上:
for (var i = 0; i < things.length; i++) (function() {
var o = things[i];
setTimeout(function() { doSomethingWith(o); }, i * 1000);
}());
否则,您已经完全使用forEach
了。请注意,您不需要关心代码中的IE&lt; = 8,因为如果您想支持它,forEach
可以简单地进行简化。
当然,ES6会给我们let
语句用新语法解决这个very common problem - 你需要使用6to5-transpiler:
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();
答案 1 :(得分:1)
我弄明白,有两种不同的方式。 第一个,您将参数绑定到此方法的调用。 它克隆函数内的参数 things [i] 并将其用作参数。
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);
}
希望它有所帮助!