JavaScript新手必看之var在for循環(huán)中的坑
一道面試題
for(var i = 0;i<5;i++){
console.log(i)
}那么以上會(huì)輸出什么呢?答案是控制臺(tái)是依次輸出0,1,2,3,4。相信大家小伙伴們都答對(duì)了。再接再厲吧,再來(lái)一道。
for(var i = 0;i<5;i++){
setTimeout(function(){
console.log(i)
})
}這次還會(huì)是同樣的結(jié)論嗎?答案是輸出5次5。這里開(kāi)始有疑惑了對(duì)吧,預(yù)期輸出應(yīng)該也是0,1,2,4才對(duì),怎么會(huì)輸出的是5呢?先開(kāi)個(gè)結(jié)論,這里是和作用域有關(guān)系的。
引申
為了更進(jìn)一步的去理解這個(gè)問(wèn)題,來(lái)一個(gè)需求吧。用一個(gè)數(shù)組去存放函數(shù),依次輸出0-4之間的數(shù)吧。
var a = []
for (var i= 0;i<2;i++){
a[i] =function(){
console.log(i)
}
}
a.forEach(_=>{
_()
})答案同樣是只能輸出5。
原因就很簡(jiǎn)單,因?yàn)槟愕拿恳粋€(gè)函數(shù)都綁定的變量i,所以每次去執(zhí)行函數(shù),都會(huì)去訪問(wèn)這個(gè)變量i,因?yàn)関ar聲明的變量,并不只局限在for循環(huán)當(dāng)中,而是在全局當(dāng)中生效了!而你又不是在循環(huán)當(dāng)中去調(diào)用它的,而是在循環(huán)之后去調(diào)用。在循環(huán)時(shí),i會(huì)伴隨著循環(huán)增長(zhǎng),此時(shí)你調(diào)用的話,前面的確實(shí)a[i]的結(jié)果是i,但a[i-1],a[i-2]...的結(jié)果也是i,因?yàn)楹瘮?shù)調(diào)用時(shí),內(nèi)部的i指向的是全局范圍內(nèi)的i。
換言之,你數(shù)組里的函數(shù)都是引用的這個(gè)全局變量i。而不是for循環(huán)里的局部變量i。
要想解決這個(gè)問(wèn)題,請(qǐng)接著往下看。
解決思路
思路:既然var聲明的是全局的變量,那么只要函數(shù)里的變量是局部的即可。
寫(xiě)法1
巧的是ES6當(dāng)中的let聲明關(guān)鍵字就是這個(gè)效果。
var a = []
for (let i= 0;i<2;i++){ //這里把原來(lái)的var聲明改成了let聲明
a[i] =function(){
console.log(i)
}
}
a.forEach(_=>{
_()
})那么可能會(huì)疑問(wèn),既然這個(gè)let聲明的i是局部變量,那么每次循環(huán)都會(huì)重新創(chuàng)建1個(gè)i吧?
是的沒(méi)錯(cuò)。
那么每次都重新創(chuàng)建的話,會(huì)不會(huì)i的值也會(huì)被重新初始化呢?
答案是不會(huì),JS引擎在for循環(huán)當(dāng)中會(huì)記住前一次結(jié)束時(shí)的i值,并且在下一次創(chuàng)建時(shí)將i賦值。
寫(xiě)法2
var a = []
for (var i= 0;i<2;i++){
a[i] = (function(i){
return function(){
console.log(i)
})(i)
}
a.forEach(_=>{
_()
})這里的寫(xiě)法就是在每次循環(huán)當(dāng)中,將循環(huán)中的i(i在不斷增長(zhǎng)),通過(guò)形參傳進(jìn)去,從而誕生出局部變量i。
附:形參傳遞的過(guò)程,基本數(shù)據(jù)類型就是將值賦給形參,而引用數(shù)據(jù)類型則是將指針賦給形參。
當(dāng)心
可能會(huì)有這樣想法的同學(xué)。這樣做只是定義了函數(shù),這個(gè)函數(shù)有1個(gè)形參i而已。這樣你調(diào)用它就變成了a[i](xxx)。
var a = []
for (var i= 0;i<2;i++){
a[i] = function(i){
console.log(i)
}
}
a.forEach(_=>{
_()
})想要傳遞參數(shù)只能是(fun(i){})(i),寫(xiě)成立即執(zhí)行函數(shù)調(diào)用它,這樣才能去給它傳值(形參)。
var a = []
for (var i= 0;i<2;i++){
a[i] = (function(i){
console.log(i)
})(i)
}
a.forEach(_=>{
_()
})而加入括號(hào)的時(shí)候,就會(huì)被執(zhí)行了,因此我們需要套一層return。這樣才能達(dá)到我們想要的效果
想要傳遞參數(shù)只能是(fun(i){})(i),寫(xiě)成立即執(zhí)行函數(shù)調(diào)用它,這樣才能去給它傳值(形參)。
而加入括號(hào)的時(shí)候,就會(huì)被執(zhí)行了,因此我們需要套一層return。這樣才能達(dá)到我們想要的效果
到此這篇關(guān)于JavaScript新手必看之var在for循環(huán)中的坑的文章就介紹到這了,更多相關(guān)var for循環(huán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript實(shí)現(xiàn)連連看連線算法
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)連連看連線算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
實(shí)用的js 焦點(diǎn)圖切換效果 結(jié)構(gòu)行為相分離
行為層是由js來(lái)實(shí)現(xiàn)的,這里的工作只是搭建了一個(gè)簡(jiǎn)單的骨架,如果要讓效果圖美觀些并符合設(shè)計(jì)要求,就要發(fā)揮css的強(qiáng)大作用了。2010-06-06
Mozilla 表達(dá)式 __noSuchMethod__
這是一個(gè)很特殊的方法,但是其存在的意義很大。不過(guò)很可惜只有firefox支持了。一個(gè)簡(jiǎn)單的例子解釋一下它的用處2009-04-04
前端存儲(chǔ)后端響應(yīng)數(shù)據(jù)超詳細(xì)的實(shí)現(xiàn)方式和注意事項(xiàng)
前端通過(guò)多種方式向后端獲取數(shù)據(jù),下面這篇文章主要介紹了前端存儲(chǔ)后端響應(yīng)數(shù)據(jù)超詳細(xì)的實(shí)現(xiàn)方式和注意事項(xiàng),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-03-03
JavaScript獲取頁(yè)面中表單(form)數(shù)量的方法
這篇文章主要介紹了JavaScript獲取頁(yè)面中表單(form)數(shù)量的方法,涉及javascript操作表單document.forms數(shù)組的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04
JavaScript初學(xué)者需要了解10個(gè)小技巧
在之前的編程語(yǔ)言排行榜中,我們?cè)榻B過(guò)轉(zhuǎn)正在即的JavaScript語(yǔ)言,正如文章中闡明的那樣,JavaScript不僅是最具活力的腳本語(yǔ)言,還是是最有用的編程語(yǔ)言之一。2010-08-08

