《JS 函数的执行时机》

95 阅读2分钟

1 解释为什么如下代码会打印 6 个 6

let i = 0
for(i = 0; i<6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}
// 6 6 6 6 6 6

Snipaste_2021-06-10_10-34-04.png

可以这样理解: 每一次 for 循环, setTimeout 都执行一次,但是里面的函数没有被执行,而是被放到了任务队列里。 for 循环执行了 6 次,就放了 6 次。当主线程执行完成后,才进入任务队列里面执行。这时候因为for循环i=6了,所以输出的全部都是6。

2 写出让上面代码打印 0、1、2、3、4、5 的方法

方法一:let关键字

for(let i = 0; i<6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}

Snipaste_2021-06-10_10-36-30.png

因为在 for 语句里用 let 声明变量是局部变量遵循块作用域,只能在当前函数中,所以每次 for 循环执行时都会生成一个单独的作用域,也会生成一个新的 i ,相当于有 6 个 i。 此时,每次执行 setTimeout() 时都会打印出对应的 i ,因此结果就是 0、1、2、3、4、5 。

方法二:闭包

 let i 
    for(i = 0;i < 6;i++){
        !function(i){
            setTimeout(()=>{
                console.log(i)
            },0)
        }(i)
    } // 0,1,2,3,4,5
  • 声明匿名函数 function(value){} 包裹setTimeout()
  • 然后再匿名函数前加上运算符!,防止生成新的全局变量
  • 在匿名函数后加个()立即调用,并把i当作参数value传入匿名函数进行调用

方法三:利用setTimeout的第三个参数,将i传进去

   let i 
    for(i = 0;i < 6;i++){
        setTimeout((value)=>{
            console.log(value)
        },0,i)
    }  // 0,1,2,3,4,5

原理 :使用setTimeout的第三个参数可以将自身传给第一个参数也就是匿名函数function(value)中,作为所需要的参数value,value可默认不写而i共传入6次,(0,1,2,3,4,5),通过匿名函数即可打印出 通常不写第三个三处,如果默认不写第三个参数,则不会传入函数

方法四:const关键字

let i 
    for(i = 0;i<6;i++){
        const a = i
        setTimeout(()=>{
            console.log(a)
        })
    } // 0,1,2,3,4,5