學(xué)習(xí)循環(huán)的時(shí)候大家應(yīng)該都試過類似下面的代碼:
for(var i=0; i<6; i++){
console.log(i)
}
很明顯運(yùn)行代碼會(huì)依次打印出0-5绝淡,代碼執(zhí)行順序也很清楚蕴掏,每次加一都執(zhí)行一次打印门坷。
但是隨著我們學(xué)習(xí)的深入我們會(huì)碰到一些并非依次執(zhí)行的例子默勾,比如用for循環(huán)給每個(gè)li添加事件
for(var i = 0; i < aLi.length; i++){
aLi[i].onclick = function(){
console.log(i)
}
}
這是運(yùn)行上面的代碼你會(huì)發(fā)現(xiàn)點(diǎn)擊每一個(gè)li都是打印最后一個(gè)的索引值休建。
因?yàn)檠h(huán)只是給每個(gè)li添加了事件乍恐,但是事件并不是立即執(zhí)行的,然而i卻是全局變量测砂,到需要執(zhí)行的時(shí)候i已經(jīng)變成了aLi.length茵烈,這類問題可以總結(jié)成如下情況:
for(var i = 0; i<6; i++){
setTimeout(()=>{//并非按順序依次執(zhí)行,會(huì)等到for循環(huán)結(jié)束再打印
console.log(i)
},0)
}
這種問題的本質(zhì)就是利用var聲明的 i 是一個(gè)全局變量砌些,全局變量在for執(zhí)行時(shí)會(huì)得到最后一個(gè)數(shù)值(aLi.length),所以會(huì)產(chǎn)生得不到每個(gè)中間值呜投,解決問題的本質(zhì)是要將 i 轉(zhuǎn)換成局部變量
方法一:var改成let
for(let i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
每次循環(huán)會(huì)多創(chuàng)建一個(gè)i讓它留在代碼空間直到代碼執(zhí)行完畢
注意let i要聲明在for的作用域里面,不然i依然不是for的局部變量
let i = 0
for(i = 0; i<6; i++){
setTimeout(()=>{
console.log(i)
},0)
}
像上面代碼i依然還是全局變量存璃,運(yùn)行結(jié)果還是打印6個(gè)6
方法二:自執(zhí)行函數(shù)可以按循環(huán)順序依次執(zhí)行仑荐,所以利用自執(zhí)行函數(shù)留下i,然后利用閉包把i傳給里面的函數(shù)
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 //也可以聲明數(shù)組把i留下來
setTimeout(()=>{
console.log(j)
},0)
}
總結(jié):循環(huán)里面的全局變量沒有按照循環(huán)順序執(zhí)行時(shí)纵东,后面是取不到當(dāng)時(shí)的值的粘招,如果要延遲執(zhí)行需要?jiǎng)?chuàng)建一個(gè)局部變量保留住當(dāng)時(shí)的值,可以使用for+let組合或者自執(zhí)行函數(shù)+閉包組合