再次看你不知道的js上的时候才发觉我之前是真的没有搞懂过let,在看到let和循环以及闭包结合的时候就蒙圈了,以下内容主要是出自该书的,能力太弱,还无法输出自己的东西。
let和循环
for循环头部的let会将变量绑定在for循环的块中,因此在循环之外的地方是无法访问到变量i的
for(let i = 0; i<5; i++){
console.log(i) // 0,1,2,3,4
}
console.log(i) // RHS右侧查询失败,就会报错referenceError
for(var j = 0; j<5; j++){
console.log(j) // 0,1,2,3,4
}
console.log(j) // 5
for循环头部的let不仅将i绑定到了for循环的块中,事实上它将其重新绑定到了循环的每一个迭代中,却把使用上一个循环迭代结束时的值重新进行赋值,对于这句话我的理解是每次循环的循环体内部都重新使用let定义了一个新变量用来存放for循环的let定义的变量i
for(let i = 0; i < 5; i++){
let j = i
console.log('打印j',j) // 0,1,2,3,4
}
let声明附属于新的作用域而不是当前的函数作用域(也不属于全局作用域),当代码中存在对于函数作用域中var声明的隐式依赖时,就会有很多隐藏的陷阱。
循环和闭包
基于以上的例子,在每个循环体内部都加入定时器
for(var j = 0; j<5; j++){
setTimeout(function(){
console.log(j) // 打印了五次5
},1000)
}
for(var j = 0; j<5; j++){
var i = j // i记录了最后一次循环体的值4
setTimeout(function(){
console.log(i) //打印了五次4
},1000)
}
循环的结束条件是5,因此最终循环结束后全局变量j的值变为了5,setTimeout是一个宏任务,因此要等到当前循环结束后才可以执行,即使定时器的时间设置为0,也是如此。实际情况是尽管循环中的五个函数是在各个迭代中分别定义的但是他们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。
for(let j = 0; j<5; j++){
setTimeout(function(){
console.log(j) // 0,1,2,3,4
},1000)
}
// 以上代码等于:每个循环都重新定义了一个变量接收循环数据,并且每次循环都创建一个块作用域
for(var j = 0; j<5; j++){
{
let i = j
setTimeout(function(){
console.log(i) // 0,1,2,3,4
},1000)
}
}
for循环头部的let声明还会有一个特殊行为,这个行为指出变量在循环过程中不止被声明一次,每次迭代都会声明,随后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。