A
for (var i=5; i>=1; i--) {
setTimeout( function timer() {
document.write(i+"<br />");
}, i*3000 );
}
// 0 0 0 0 0
// 保存执行后,3秒后弹出第一个console(0),之后隔3秒弹出第二个,依次按照时间执行。
B
for (var i=5; i>=1; i--) {
setTimeout( function timer() {
document.write(i+"<br />");
}, 3000 );
}
// 0 0 0 0 0
// 保存执行后,3秒后所有的console一齐弹出
C
for (var i=5; i>0; i--) {
!function (i) {
setTimeout( function timer() {
console.log(i);
}, i*3000 );
}(i)
}
// 1 2 3 4 5 每隔3秒依次弹出
这里我抽象一下setTimeout
的队列执行结构:
注意,时间设定与闭包没有关系!
当所有的非队列结构代码都执行完毕,函数调用栈清空后,这些setTimeout才会进入队列数据栈等待执行
。而时间,就是按照图中那样:
A:
A与B的区别就是时间i*3000
与3000
的区别。setTimeout
的定义是与正常的JS执行是同步的,而函数的执行是异步的,所以,根据不同的i
值,setTiemout
在时间轴上已经定义了函数。
而且可以发现,时间设置里的i
是根据for
循环变化的(5 4 3 2 1),这也反证了setTimeout
的定义时是与主JS程序同步。所以根据不同的i*3000,在时间轴上定义匿名函数。执行时,每隔3秒,弹出一个0。
当然,由于匿名函数指向的是全局变量i,此时主JS执行结束早已为0,所以弹出0。
B:
在B代码中,没有设置i
与时间的计算,所以循环中产生的5个匿名函数都是定义在3秒这个时间节点上,当主JS代码执行完毕,开始执行队列结构栈中的函数时,5个函数在3秒后同时执行,所以会弹出5个0。而对于匿名函数中的i取值,这里自然是来自全局变量中的i,主JS程序执行完毕i自然运算完毕值为0,所以5个i都是0;
C:
引入闭包的情况和B其实是类似的,时间设定与匿名函数执行始终井水不犯河水,所以时间轴与B相同,不同的是匿名函数里的i
值。
图中嵌套的正方形就抽象为自调用函数,每一次的循环中产生的i值,会传入自调用函数,保存在其变量对象中。由于匿名函数里的是自调用函数的闭包,匿名函数引用了变量对象里的i
,所以每个自调用函数的变量对象不会在执行完毕后被回收,得以保持继续被匿名函数引用着。最后实现最终效果。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。