创建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
相关产品推荐
相关产品推荐

