You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建div元素的for循环中let与var的差异及结果原因问询

关于for循环中let/var差异的详细解答

嘿,这个问题可太经典了——刚好是ES6引入let解决var历史遗留问题的核心场景,咱们把它掰碎了说清楚~

先拆解var的问题

当你用var声明循环变量i时,本质是这几个特性在起作用:

  • var是函数级作用域(如果在全局环境下,就直接属于全局作用域),所以整个循环过程中只有同一个i变量在反复被赋值。
  • var会触发变量提升,而且在全局作用域下,这个变量会自动挂载到window对象上(你可以在控制台直接输入window.i看到它)。

最关键的点在于:点击事件的回调是异步执行的——等你真的点击div时,循环早就跑完了,此时i已经被自增到45(因为循环条件是i < 45,最后一次循环后i变成45才退出)。所有回调函数引用的都是同一个i,所以不管点哪个div,控制台都会输出45。

再看let的解决逻辑

let是ES6专门为块级作用域设计的声明方式,针对循环场景有特殊处理:

  • let属于块级作用域,每次循环迭代时,都会创建一个新的i变量绑定——简单说就是每一轮循环都有自己独立的i。
  • 当你给div绑定点击回调时,这个回调引用的是当前迭代的那个i,所以点击不同的div,就会输出对应迭代的数值(0到44)。

和window对象有关吗?

有关系,但这是结果而非原因:

  • 在全局作用域下,var声明的变量会自动成为window的属性;而let声明的全局变量不会挂载到window上(你可以试试在控制台输入window.i,用let的话会返回undefined)。
  • 但核心差异还是作用域规则和变量绑定方式,window只是这个差异带来的附属表现而已。

可运行测试代码

<!DOCTYPE html>
<html>
<body>
  <div id="container"></div>
  <script>
    const container = document.getElementById('container');
    
    // 用var的情况:点击所有div输出45
    for (var i = 0; i < 45; i++) {
      const div = document.createElement('div');
      div.textContent = `Div ${i}`;
      div.style.padding = '8px';
      div.style.border = '1px solid #eee';
      div.addEventListener('click', () => {
        console.log(i);
      });
      container.appendChild(div);
    }

    // 换成let试试:点击对应div输出0-44
    // for (let i = 0; i < 45; i++) {
    //   const div = document.createElement('div');
    //   div.textContent = `Div ${i}`;
    //   div.style.padding = '8px';
    //   div.style.border = '1px solid #eee';
    //   div.addEventListener('click', () => {
    //     console.log(i);
    //   });
    //   container.appendChild(div);
    // }
  </script>
</body>
</html>

总结关键点

  • var:函数作用域、变量提升、全局var挂载window、循环共享同一变量
  • let:块级作用域、无变量提升、全局let不挂载window、循环每次迭代创建新变量绑定

内容的提问来源于stack exchange,提问作者Planet.Megan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:45:36