for循环中的let声明和var声明的区别

0 阅读1分钟

使用 var 的情况


for (var i = 0; i < 5; ++i) {
    setTimeout(() => console.log(i), 0)
}
// 输出:55555

为什么会这样?

  1. var 的函数作用域特性:
  • var 声明的变量具有函数作用域(或全局作用域)

  • 整个循环中只有一个 i 变量

  1. 事件循环机制:
  • setTimeout 是异步函数,会被放入任务队列

  • 循环是同步执行的,会立即完成

  • 当循环结束时,i 的值已经变成了 5

  1. 执行时序:

// 执行顺序:
// 1. 循环开始,i = 0,注册第一个 setTimeout
// 2. i++,i = 1,注册第二个 setTimeout  
// 3. i++,i = 2,注册第三个 setTimeout
// 4. i++,i = 3,注册第四个 setTimeout
// 5. i++,i = 4,注册第五个 setTimeout
// 6. i++,i = 5,循环条件不满足,退出循环
// 7. 现在开始执行 setTimeout 的回调函数
// 8. 所有回调函数访问的都是同一个 i,此时 i = 5

使用 let 的情况


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

为什么 let 能解决这个问题?

  1. let 的块级作用域特性:
  • let 具有块级作用域

  • JavaScript 引擎会为每次循环迭代创建一个新的 i 变量

  1. 变量绑定机制:

// 相当于:
{
    let i = 0;  // 第一次迭代的 i
    setTimeout(() => console.log(i), 0); // 引用第一次迭代的 i
}
{
    let i = 1;  // 第二次迭代的 i  
    setTimeout(() => console.log(i), 0); // 引用第二次迭代的 i
}
// ... 以此类推