为什么需要 IIFE 来创建新范围?
why is IIFE needed to create a new scope?
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
块内的一半。
用 var
声明的 i
被提升。变量不会自动将它们的范围绑定到内部函数;除非内部函数明确地具有 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
调用 ,函数将使用 new 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);
}
所以,在这种情况下,您可以看到 "captured" 值为 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(或任何 internet exploder 支持的)而使用转译器,你会看到转译版本是
var _loop = function _loop(i) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
};
for (var i = 1; i <= 5; i++) {
_loop(i);
}
这看起来非常像以前版本的代码
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
块内的一半。
var
声明的 i
被提升。变量不会自动将它们的范围绑定到内部函数;除非内部函数明确地具有 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
调用 ,函数将使用 new 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);
}
所以,在这种情况下,您可以看到 "captured" 值为 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(或任何 internet exploder 支持的)而使用转译器,你会看到转译版本是
var _loop = function _loop(i) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
};
for (var i = 1; i <= 5; i++) {
_loop(i);
}
这看起来非常像以前版本的代码