您的位置:首页 > Web前端 > JavaScript

循环中的闭包

2015-06-01 05:18 441 查看
for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);  
    }, 1000);
}

不过这段代码执行的结果是10,而不是0,1,2,3,4,5,6,7,8,9。

为什么呢?

(引自:http://www.douban.com/note/293295975/

因为setTimeout是异步的!
   你可以想象由于setTimeout是异步的, 因此我们将这个for循环拆成2个部分
   第一个部分专门处理 i 值的变化, 第二个部分专门来做setTimeout
   因此我们可以得到如下代码
   // 第一个部分
   i++;
   ...
   i++; // 总共做10次

   // 第二个部分
   setTimeout(function() {
      console.log(i);
   }, 1000);
   ...
   setTimeout(function() {
      console.log(i);
   }, 1000); // 总共做10次

   这样一拆后, 我相信你肯定知道之前那个for循环的运行结果了.
   由于循环中的变量 i 一直在变, 最终会变成10, 而循环每每执行setTimeout时, 其中的方法还没有真正运行, 等真正到时间执行时, i 的值已经变成 10 了!
   i 变化的整个过程是瞬间完成的, 总之比你异步要快, 就算你setTimout是0毫秒也一样, 会先于你执行完成.

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


避免引用错误

为了正确的获得循环序号,最好使用 匿名包裹器译者注:其实就是我们通常说的自执行匿名函数)。
for(var i = 0; i < 10; i++) {
    (function(e) {
        setTimeout(function() {
            console.log(e);  
        }, 1000);
    })(i);
}


外部的匿名函数会立即执行,并把 
i
 作为它的参数,此时函数内 
e
 变量就拥有了 
i
 的一个拷贝。

当传递给 
setTimeout
 的匿名函数执行时,它就拥有了对 
e
 的引用,而这个值是不会被循环改变的。

有另一个方法完成同样的工作;那就是从匿名包装器中返回一个函数。这和上面的代码效果一样。
for(var i = 0; i < 10; i++) {
    setTimeout((function(e) {
        return function() {
            console.log(e);
        }
    })(i), 1000)
}

以上引自(密码花园:http://bonsaiden.github.io/JavaScript-Garden/zh/#function.closures

上面的代码看起来都比较费劲,下面我把代码拆分开,更容易理解一些。

window.onload= function(){
for(var i=0; i<5; i++){
/*(function(j){
setTimeout(function(){  //setTimeout是在循环结束后才被“异步”调用的
console.log(j+'');
},100);
})(i);*/

            test(i);
}
}

function test(j){
setTimeout(function(){
console.log(j+'');
},100);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  闭包 javascript 异步