for循环绑定事件时,var和let声明循环变量的区别

时间:2021-08-09 20:28:30

在理解var、let、const三者在声明变量的区别时,遇到了一道十分有意思的题:

<body>
<ul>
	<li>第一个li</li>
	<li>第二个li</li>
	<li>第三个li</li>
	<li>第四个li</li>
	<li>第五个li</li>
</ul>

<script>

var liList = document.querySelectorAll('li') // 共5个li
for(var i=0; i<liList.length; i++){
  liList[i].onclick = function(){
    console.log(i)
  }
}	
</script>
</body>

以上代码中循环变量的声明用var和let会有什么不同的效果?
结果肯定相信很多人都知道:用var时,都打印5。用let时,打印0、1、2、3、4。


为什么会这样,估计很多人就不知道了吧。于是我便讲讲我的理解:


关键点是:1.理解作用域链。
  2.var和let的作用域。
  3.变量提升。
详细讲解一下:
铺垫知识:
1.var声明变量是函数作用域,而let声明变量是语句块作用域;
2.var提升到函数定义顶部,此处是全局作用域顶部;let提升到语句块顶部,此处是for循环第一行。
3.for( let i = 0; i< 5; i++) 这句话的圆括号之间,有一个隐藏的作用域(用var时没有)。
  for( let i = 0; i< 5; i++) { 循环体 } 在每次执行循环体之前,JS 引擎会把 i 在循环体的上下文中重新声明及初始化一次。

正式讲解:
1.i的声明被提升。
2.当运行for循环时为i赋值,并为每个li绑定事件( 注意:运行for循环时只是绑定了事件但是并没有执行事件)。
3.当触发事件时( 注意:此时for循环执行完了),现在需要控制台打印i的值,于是i便沿着作用域链寻找它的值。
4.当用var声明时,i会在全局作用域中找到它的值,为5.
5.当用let声明时,i会在for的第一行找到它的值,每次的值不一样,分别为0、1、2、3、4.


注:当用let时代码可以这样理解:
var liList = document.querySelectorAll('li') // 共5个li
for( let i=0; i<liList.length; i++){
  let i = 隐藏作用域中的i 
  liList[i].onclick = function(){
    console.log(i)
  }
}

在不补充点关于变量提升的知识

1.let 的「创建」过程被提升了,但是初始化没有提升。(此时变量进入暂时死区)
2.var 的「创建」和「初始化」都被提升了。
3.function 的「创建」「初始化」和「赋值」都被提升了。
4.const 和 let 只有一个区别,那就是 const 只有「创建」和「初始化」,没有「赋值」过程。(也会进入暂时死区)。