函数调用的时机不同,运行结果也不同。

为什么如下代码会打印 6 个 6,而不是 0,1,2,3,4,5?

  1. /* 例 1 */
  2. let i = 0
  3. for(i = 0; i<6; i++){
  4. setTimeout(()=>{ // 延时函数
  5. console.log(i)
  6. },0)
  7. }

运行结果如下:
image.png

setTimeout() 函数是干嘛的?

MDN 给出的描述是:

  • **setTimeout()** 方法设置一个定时器,该定时器在定时器到期后执行一个函数或指定的一段代码。

白话理解:

  • 凡是放在这个函数中的东西,都过一会再做,至于过多久,可以通过设定毫秒数来调节。

😉 如果 **setTimeout** 延时设置为 0 呢?

  • 如代码例 1 中 **setTimeout** 作为延时函数设置延时为 0 时,意思是尽快执行完 for 循环,然后立马执行 setTimeout 打印出 i 的值。
  • 循环执行完时 i = 6 ,由于 for 循环了6次,也就是 i = 6 之后再去打印 6 次 i,所以打印结果为 6 个 6

如何让例 1中的代码 打印出 0,1,2,3,4,5?

  1. /* 把 let 直接写到 for 循环里 */
  2. for(let i = 0; i<6; i++){
  3. // let i = 隐藏作用域中的i
  4. setTimeout(()=>{
  5. console.log(i)
  6. },0)
  7. } // 打印结果为 0,1,2,3,4,5

在 for循环表达式中使用 let声明表达式变量

  1. **for( let i = 0; i< 6; i++)** 这句话的圆括号之间,有一个隐藏的作用域
  2. **for( let i = 0; i< 6; i++) { 循环体 }** 在每次执行循环体之前,JS 引擎会把 **i** 在循环体的上下文中重新声明及初始化一次。
  3. 因为 **let** 有自己的作用域块,所以在 for循环表达式中使用 **let** 它的每一个值都会单独存在一个独立的作用域中不会被覆盖掉。
    • 每次进入 for 循环时都把 i 复制一份,就是第一次循环把 i 留在这个空间,后面的每一次都这样操作就有 6 个新的 i,再加上本身 i = 0 的这个 i ,一共就有 7 个 i
    • 这样就是 i 从 0 到 6,每次都把 i 打出来,最后就是打出 0~5 ,因为 i = 6 时就跳出了循环。

      参考资料: 方方:我用两个月的时间理解 let

除了使用 for let 配合,还有什么其他方法可以打印出 0、1、2、3、4、5?如下:

  1. /* 1.闭包 */
  2. let i
  3. for(i = 0; i<6; i++){
  4. !function(j){
  5. setTimeout(()=>{
  6. console.log(j)
  7. },0)
  8. }(i)
  9. } // 打印: [0,1,2,3,4,5]
  1. /* 2.数组的push */
  2. var arr1 = [];
  3. var n = 0;
  4. for(var i = 0; i<6; i++)
  5. {
  6. n += 1;
  7. arr1.push(n-1)
  8. }
  9. console.log(arr1) // 打印: [0,1,2,3,4,5]
  1. /* 3.利用 setTimeout 的第三个参数,将i传进去 */
  2. let i
  3. for(i = 0; i<6; i++){
  4. setTimeout((value)=>{
  5. console.log(value)
  6. },0,i)
  7. } // 打印: [0,1,2,3,4,5]
  1. /* 4.利用 const 关键字 */
  2. let i
  3. for(i=0; i<6; i++){
  4. const x = i
  5. setTimeout(() => {
  6. console.log(x)
  7. })
  8. }