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.οnlοad= 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);
}