setTimeout、var与for循环


ES6中的let关键字的出现必然有它的原因,var关键字的缺陷是什么。

有趣的小现象

Case1:

1
2
3
4
5
for(var i = 0; i <= 3; i++){
setTimeout(function(){
console.log(i)
}, i * 1000)
}

Case 2:

1
2
3
4
5
for(var i = 0; i <= 3; i++){
setTimeout(function (){
console.log(i)
}(i), i * 1000)
}

Case 3:

1
2
3
4
5
6
7
for(var i = 0; i <= 3; i++){
(function (i){
setTimeout(function(){
console.log(i)
}, i * 1000)
})(i)
}

把这些代码放到浏览器上运行可以看到运行结果分别是

  1. 每隔1s输出一次4
  2. 几乎是马上输出了0-3
  3. 先输出0,1s后输出1,2s后输出2…3s后输出3

现象解析:

  1. 第一个是比较常见的新手错误,以为这样可以达到0s输出0,1s输出1…这样的效果。其实并不然,根据js的异步机制,异步代码会被放入一个事件队列,等到所有其他代码执行后才进行,而不会阻塞线程,而setTimeout就是一个异步函数。所以setTimeout会在for循环结束后才被调用,而且每隔一秒会向异步的事件队列中插入一次相应的回调函数,而在console.log(i)真正运行的时候,i已经变成4了, 所以就是每隔1s输出一次4

  2. case 2中的setTimeout第一个参数是一个立即执行函数(什么是立即执行函数),所以每一次循环都立即执行,setTimeout的第二个参数并没有起作用。所以每一次循环都会输出当前i,所以几乎是马上而同时输出了0-3

  3. case 3中相当于for循环中的循环体是一个立即执行函数,这里有闭包的效果,这个i这是就是一个局部变量,每一次执行时i的值都不一样,就会出现相应的情况.

感谢segmentfault的for循环中setTimeout问题以及对于javascript里的块作用域不是很理解,比如下面的这段代码为什么会输出5个6?