带有闭包的JavaScript范围:帮助我理解

时间:2010-09-08 21:10:32

标签: javascript scope closures

运行以下代码:

for (var i=0; i<3; i++) { 
   setTimeout( function() { console.log(i); } , 500 ); 
}

输出“3”三次。在创建内部函数时,它输出的最终值为i,而不是i的值。

如果我希望输出为1,2和3,我该如何编写此代码?如何在定义函数时使用i的值而不是其最终值?

4 个答案:

答案 0 :(得分:6)

for (var i=0; i<3; i++) {
   setTimeout( function(val) { return function() { console.log(val); } }(i), 500 );
}

因此,在setTimeout时(在我们定义setTimeout的函数时),我们将val作为参数调用匿名函数。这为每个函数调用创建了一个闭包,将val的值存储在我们刚刚调用的函数的范围内。我使用了self-invoking function,它会立即生成closure

在您提供的代码中,代码创建了一个闭包,但是对于整个代码的较大范围,因此i对于整个代码是本地的,这意味着在运行时,匿名函数将使用代码其余部分使用的变量i

答案 1 :(得分:4)

function f(i){
  return function(){console.log(i);};
}

for (var i=0; i<3; i++) { 
   setTimeout( 
     f(i)
   , 500 ); 
}

答案 2 :(得分:2)

显式闭包的现代替代方法(当你有双重包装函数时可能会有点毛茸茸的读取)是Function#bind。对于尚未执行ECMAScript第五版的浏览器hacked in support,您可以说:

for (var i=0; i<3; i++) {
    setTimeout(function(i) { console.log(i); }.bind(window, i), 500);
}

window是函数内this的值(这里不需要this,所以我们只使用默认的全局对象)。如果您只是调用另一个函数/方法,就像使用console.log一样,您可以使用它来完全删除函数表达式:

for (var i=0; i<3; i++) {
    setTimeout(console.log.bind(console, i), 500);
}

答案 3 :(得分:1)

替代:

for (var i=0; i<3; i++) {
   (function(val){
       setTimeout(function() {
           console.log(val);
       },500)
   }(i));
}