学习循环的时候大家应该都试过类似下面的代码:
for(var i=0; i<6; i++){
console.log(i)
}
很明显运行代码会依次打印出0-5,代码执行顺序也很清楚,每次加一都执行一次打印。
但是随着我们学习的深入我们会碰到一些并非依次执行的例子,比如用for循环给每个li添加事件
for(var i = 0; i < aLi.length; i++){
aLi[i].onclick = function(){
console.log(i)
}
}
这是运行上面的代码你会发现点击每一个li都是打印最后一个的索引值。
因为循环只是给每个li添加了事件,但是事件并不是立即执行的,然而i却是全局变量,到需要执行的时候i已经变成了aLi.length,这类问题可以总结成如下情况:
for(var i = 0; i<6; i++){
setTimeout(()=>{//并非按顺序依次执行,会等到for循环结束再打印
console.log(i)
},0)
}
这种问题的本质就是利用var声明的 i 是一个全局变量,全局变量在for执行时会得到最后一个数值(aLi.length),所以会产生得不到每个中间值,解决问题的本质是要将 i 转换成局部变量
方法一:var改成let
for(let i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
每次循环会多创建一个i让它留在代码空间直到代码执行完毕
注意let i要声明在for的作用域里面,不然i依然不是for的局部变量
let i = 0
for(i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
像上面代码i依然还是全局变量,运行结果还是打印6个6
方法二:自执行函数可以按循环顺序依次执行,所以利用自执行函数留下i,然后利用闭包把i传给里面的函数
for(var i = 0; i<6; i++){
!function(a) {
setTimeout(()=>{
console.log(a)
},0)
}(i)
}
方法三:将每次i的值变成局部变量留下来
for(var i = 0; i<6; i++){
let j = i //也可以声明数组把i留下来
setTimeout(()=>{
console.log(j)
},0)
}
总结:循环里面的全局变量没有按照循环顺序执行时,后面是取不到当时的值的,如果要延迟执行需要创建一个局部变量保留住当时的值,可以使用for+let组合或者自执行函数+闭包组合