在这里创建闭包的替代方法是什么?
What are the alternatives to creating a closure here?
假设我们有一个简单对象列表:
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<9,我们可以依赖 .forEach()
:
things.forEach(function(o, i) {
var o = things[i];
setTimeout(function() { doSomethingWith(o); }, i * 1000);
});
但是,无论如何,我们最终在这种情况下创建了一种闭包,将匿名函数传递给 forEach()
。
有没有办法在没有闭包或函数引用的情况下完成此操作?
根本问题有三方面:
- 不太重要的是:传递给
setTimeout()
(或任何它可能是什么)的函数引用让你(我)感觉 就像你正在创建一个闭包。所以,我倾向于忘记外部封闭。
- 更重要的是: 额外的 function/closure 声明鼓励 "arrow code." 对于复杂操作,随着代码从屏幕上迁移,复杂操作的代码可读性会迅速恶化...这可能由 IE>9 中的
.forEach()
解决,除非应用程序或组织风格指南规定了换行符和闭包缩进。
- 最 重要的是: 我很确定有一种简单的方法可以处理这个问题。我觉得现在想不起来很傻。
也许更好的提问方式是:在我们开始强制创建闭包之前,我们到底做了什么?
我不认为在这里使用闭包有什么问题。它们是 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 会给我们 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);
}
What the devil did we all do before we all started compulsively creating closures?
我们使用了全局状态,并以不同的方式解决了我们的问题。例如,您的案例将由半递归函数更好地解决:
var i = 0;
function next() {
if (i < things.length) {
doSomethingWith(things[i++]);
setTimeout(next, 1000);
}
}
next();
我弄清楚了,有两种不同的方法。
第一个,您 将参数 绑定到此方法的调用。
它在函数中克隆参数 things[i] 并将其用作参数。
for (var i = 0; i < things.length; i++) {
var o = things[i];
setTimeout(doSomethingWith.bind(null, things[i]), i * 1000);
}
还有第二种方式,
setTimeout 在参数时间之后,以毫秒为单位,接受你将要调用的函数的参数,它也会复制定义时的值,所以变量值可以在之后改变,setTimeout 将保证正确值将作为参数传递。
for (var i = 0; i < things.length; i++) {
var o = things[i];
setTimeout(function(param) { doSomethingWith(param); }, i * 1000, o);
}
希望对您有所帮助!
假设我们有一个简单对象列表:
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<9,我们可以依赖 .forEach()
:
things.forEach(function(o, i) {
var o = things[i];
setTimeout(function() { doSomethingWith(o); }, i * 1000);
});
但是,无论如何,我们最终在这种情况下创建了一种闭包,将匿名函数传递给 forEach()
。
有没有办法在没有闭包或函数引用的情况下完成此操作?
根本问题有三方面:
- 不太重要的是:传递给
setTimeout()
(或任何它可能是什么)的函数引用让你(我)感觉 就像你正在创建一个闭包。所以,我倾向于忘记外部封闭。 - 更重要的是: 额外的 function/closure 声明鼓励 "arrow code." 对于复杂操作,随着代码从屏幕上迁移,复杂操作的代码可读性会迅速恶化...这可能由 IE>9 中的
.forEach()
解决,除非应用程序或组织风格指南规定了换行符和闭包缩进。 - 最 重要的是: 我很确定有一种简单的方法可以处理这个问题。我觉得现在想不起来很傻。
也许更好的提问方式是:在我们开始强制创建闭包之前,我们到底做了什么?
我不认为在这里使用闭包有什么问题。它们是 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 会给我们 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);
}
What the devil did we all do before we all started compulsively creating closures?
我们使用了全局状态,并以不同的方式解决了我们的问题。例如,您的案例将由半递归函数更好地解决:
var i = 0;
function next() {
if (i < things.length) {
doSomethingWith(things[i++]);
setTimeout(next, 1000);
}
}
next();
我弄清楚了,有两种不同的方法。 第一个,您 将参数 绑定到此方法的调用。 它在函数中克隆参数 things[i] 并将其用作参数。
for (var i = 0; i < things.length; i++) {
var o = things[i];
setTimeout(doSomethingWith.bind(null, things[i]), i * 1000);
}
还有第二种方式,
setTimeout 在参数时间之后,以毫秒为单位,接受你将要调用的函数的参数,它也会复制定义时的值,所以变量值可以在之后改变,setTimeout 将保证正确值将作为参数传递。
for (var i = 0; i < things.length; i++) {
var o = things[i];
setTimeout(function(param) { doSomethingWith(param); }, i * 1000, o);
}
希望对您有所帮助!