2022.8.2
在 JS 中有一个奇特的setTimeout(),将它放在下面的函数中,能让结果变得很有趣:
let i = 0
for(i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
不了解setTimeout()的人,可能会认为上述函数的运行结果是 0、1、2、3、4、5,实际上它会打印出 6 个 6。
1.为什么上面的代码会打印出 6 个 6?
因为setTimeout()的逻辑是,执行完当前的所有代码后,立即执行setTimeout()里的代码。
而在文章开头的代码中,所谓“当前的所有代码”,就包含了整个 for 循环。即当 for 循环结束后setTimeout()里的代码才会被执行。
在 for 循环结束后,setTimeout()执行前,内存中有些什么呢?由于 for 循环执行了 6 次(i 为 0~5 总共 6 次),此时积累了 6 个等待执行的setTimeout(),同时还有值为 6 的 i。
终于轮到 6 个setTimeout()执行,它们把此时值为 6 的 i ,依次用console.log(i)打印了出来,于是,我们就看到了 6 个 6。
2.有什么方法,可以配合 setTimeout() 打印出 0、1、2、3、4、5?
第一种方法是 for let 配合:
for(let i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
第二种方法,是利用立即执行函数的局部变量 j,来单独接受每个循环里 i 的值,然后令setTimeout()依次打印出 j。
let i = 0
for(i = 0; i!=6; i++){
!function(){
let j = i
setTimeout(()=>{
console.log(j)
},0)
}()
}
当然,我还无法理解原理,为什么立即执行函数跟内置let i = 0的效果一样?它们之间有什么关系?可惜,我现在没有能力深究。
第三种方法略麻烦,是将i++的流程移动到setTimeout()里面,但是需要在 for 循环结束后将 i 重置为 0:
let i = 0
for(i = 0; i<6; i++){
setTimeout(()=>{
console.log(i++)
},0)
}
i = 0