我的一个朋友和我目前正在讨论JS中的闭包和不闭包。我们只想确保我们正确理解它。
让我们来看这个例子。我们有一个计数循环,希望延迟在控制台上打印计数器变量。因此,我们使用setTimeout
和闭包捕获计数器变量的值,以确保其不会打印N倍于值N的值。
没有闭包或接近闭包的错误解决方案是:
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
当然,它将i
在循环后输出10倍的值,即10。
所以他的尝试是:
for(var i = 0; i < 10; i++) {
(function(){
var i2 = i;
setTimeout(function(){
console.log(i2);
}, 1000)
})();
}
按预期打印0到9。
I told him that he isn't using a closure to capture i
, but he insists that he is. I proved that he doesn't use closures by putting the for loop body within another setTimeout
(passing his anonymous function to setTimeout
), printing 10 times 10 again. The same applies if I store his function in a var
and execute it after the loop, also printing 10 times 10. So my argument is that he doesn't really capture the value of i
, making his version not a closure.
My attempt was:
for(var i = 0; i < 10; i++) {
setTimeout((function(i2){
return function() {
console.log(i2);
}
})(i), 1000);
}
So I capture i
(named i2
within the closure), but now I return another function and pass this around. In my case, the function passed to setTimeout really captures i
.
Now who is using closures and who isn't?
Note that both solutions print 0 to 9 on the console delayed, so they solve the original problem, but we want to understand which of those two solutions uses closures to accomplish this.
让我们看一下两种方式:
声明并立即执行
setTimeout()
在其自身上下文中运行的匿名函数。的当前值i
是通过将副本复制为i2
first 来保留的;它之所以有效是因为立即执行。声明内部函数的执行上下文,从而将的当前值
i
保存到i2
;这种方法还使用立即执行来保留值。重要
应该提到的是,两种方法之间的运行语义是不同的。您的内部函数将传递给
setTimeout()
他,而他的内部函数将setTimeout()
自己调用。将两个代码包装在另一个代码中
setTimeout()
并不能证明只有第二种方法使用了闭包,开始的东西并不相同。结论
两种方法都使用闭包,所以这取决于个人喜好。第二种方法更容易“移动”或概括。