From You Don't Know JS:
for (var i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i );
}, i*1000 );
}
给
6
6
6
6
6
但是使用这样的 IIFE
for (var i=1; i<=5; i++) {
(function(){
var j = i;
setTimeout( function timer(){
console.log( j );
}, j*1000 );
})();
}
给
1
2
3
4
5
我的问题:为什么不
for (var i=1; i<=5; i++) {
setTimeout( function timer(){
var j = i;
console.log( j );
}, i*1000 );
}
或
for (var i=1; i<=5; i++) {
function timer() {
var j = i;
console.log(j);
}
setTimeout(timer, i*1000 );
}
像 IIFE 示例一样工作?在我看来,他们都有一个带有新变量j
的function
声明,这不会创建一个具有特定设置i
的新词汇范围吗?
IIFE 的重要部分是它立即运行;在i
更改之前,它会读取其值并将其放入新变量中。其他示例中的函数读取i
(function timer()
(不会立即运行,它放入新变量中的值是更改后的i
值。
此外,在 ES6 中,您可以只let i = …
而不是var i = …
,如果没有 IIFE 或j
,它可以正常工作:
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
因为let
有块作用域而不是函数作用域,并且在for
循环的初始化部分声明的变量算作在for
块的一半内。
i
,用var
声明,被吊起。变量不会自动将其作用域绑定到内部函数;除非内部函数显式具有var i
或i
参数(从而定义绑定到内部函数范围的新i
(,否则i
将继续引用外部作用域中的提升i
。
例如,如果你愿意,你可以像这样做你想做的事情:
for (var i=1; i<=5; i++) {
setTimeout( function timer(i){
console.log( i );
}, i*1000, i );
}
(要setTimeout
的第三个参数是调用函数(第二个参数(的函数(
这意味着timer
将在迭代过程中使用i
调用,并且该函数将使用一个新的i
,绑定到函数的作用域,通过参数初始化。
不过,这是一个非常糟糕的主意 - 最好使用const
和let
,它们具有块作用域而不是函数作用域,最好不要隐藏外部变量。
这种IIFE
for (var i=1; i<=5; i++) {
(function(){
var j = i;
setTimeout( function timer(){
console.log( j );
}, j*1000 );
})();
}
经常写成
for (var i=1; i<=5; i++) {
(function(j){
setTimeout( function timer(){
console.log( j );
}, j*1000 );
})(i);
}
因此,您可以看到在这种情况下i
"捕获"值
您可以在没有 IIFE 的情况下做同样的事情
for (var i=1; i<=5; i++) {
function timer(j) {
setTimeout(function() {
console.log(j);
}, j * 1000 );
}
timer(i);
}
当然,这相当于
function timer(j) {
setTimeout(function() {
console.log(j);
}, j * 1000 );
}
for (var i=1; i<=5; i++) {
timer(i);
}
如果使用ES2015+,则可以使用let
for (let i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i );
}, i*1000 );
}
现在,如果您因为需要支持 ES5(或任何互联网爆炸器支持(而使用转译器,您将看到转译版本是
var _loop = function _loop(i) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
};
for (var i = 1; i <= 5; i++) {
_loop(i);
}
这看起来非常像以前版本的代码