let理解:
- let 声明的变量支持块级作用域
- let 不存在变量提升
- let 存在暂时死区(不能在初始化之前,使用变量)
根据下面代码块深入理解:
(涉及块级作用域、变量提升相关知识)
// 代码段1
var liLists = document.querySelectorAll('li') // 共5个li
for( var i=0; i<liLists.length; i++){
liLists[i].onclick = function(){
console.log(i)
}
}
众所周知最后 i
的打印结果都是5
,而不是想象中的0、1、2、3、4
原因(仅个人理解,哪里有错误望各位大神指正):
- onclick事件触发的函数,并非立即执行,当执行时,for循环一定已经遍历完了。
- 那么很多人就有疑问: 遍历完后,
i
应该是4
, 可结果为什么是5
,因为var存在变量提升,你应该把上面的代码这样理解:
// 代码段2
var liLists;
var i;
liLists = document.querySelectorAll('li') // 共5个li
for( i=0; i<liLists.length; i++){
liLists[i].onclick = function(){
console.log(i)
}
}
- 当遍历到最后(也就是很多人理解的
i
为4
时),i
依旧执行了最后一次i++
, 只是没有满足for
循环的遍历条件, 且没有进入到for
循环。 但是经过变量提升后的的i
已经变成了5
。 - 最后当onclick时间发生的时候,所调用的函数中引用了
i
变量,这个时候就涉及到了作用域,当自身作用域中找不到该变量时,就会去上一层找,上一层也找不到就会去全局中。全局的i
为5
, 所以结果都为5
解决的办法:
1. var
改为let
后, 依次触发事件, 所得到的结果为0,1,2,3,4
// 代码段3
var liLists = document.querySelectorAll('li') // 共5个li
for( let i=0; i<liLists.length; i++){
liLists[i].onclick = function(){
console.log(i)
}
}
let
声明的变量不存在变量提升for( let i = 0; i< 5; i++)
这句话的圆括号之间,有一个隐藏的作用域for( let i = 0; i< 5; i++) { 循环体 }
在每次执行循环体之前,JS 引擎会把i
在循环体的上下文中重新声明及初始化一次。
也就是说上面的代码段3可以近似近似近似地理解为
// 代码段4
var liLists = document.querySelectorAll('li') // 共5个li
for( let i=0; i<liLists.length; i++){
let i = 隐藏作用域中的i // 看这里看这里看这里
liLists[i].onclick = function(){
console.log(i)
}
}
那样的话,5 次循环,就会有 5 个不同的 i,console.log 出来的 i 当然也是不同的值。
2. 使用 立即执行函数+闭包
function demo(){
let arr =[]
for(var i = 1; i<=3 ; i++){
arr.push(
(function(n) {
return function(){
return n
}
})(i)
)
}
return arr
}
console.log(demo()[1]());
- 这样当遍历时,
i
就会被及时的留存,这里面就应用到了 闭包+立即执行函数
以上内容均借鉴于以下文章, 小编只是融合了一小部分自己的理解。
出处:
https://www.jianshu.com/p/0f49c88cf169
https://www.liaoxuefeng.com/wiki/1022910821149312/1023021250770016