有時(shí)做題時(shí)坐梯,不難發(fā)現(xiàn)有些題目在實(shí)際開(kāi)發(fā)工作中可能并不會(huì)如此書(shū)寫(xiě)代碼徽诲,但,它能考察你對(duì)基礎(chǔ)知識(shí)的掌握程度吵血。就好比本文要說(shuō)的變量名與函數(shù)名重復(fù)的情況谎替。講解前先看題,如下:
這道題結(jié)果輸出是多少呢践瓷?有小伙伴可能會(huì)說(shuō)輸出10院喜,也有小伙伴可能會(huì)覺(jué)得是輸出20,那么晕翠,究竟是多少呢喷舀?又是為什么呢?在講解答案前淋肾,我們先做點(diǎn)知識(shí)點(diǎn)儲(chǔ)備工作:
- 變量聲明提升
在執(zhí)行函數(shù)test
時(shí)硫麻,函數(shù)作用域內(nèi)有非聲明變量x = 20;
語(yǔ)句,有些小伙伴可能會(huì)認(rèn)為在執(zhí)行完函數(shù)test
后樊卓,函數(shù)內(nèi)部非聲明變量x
被隱式地創(chuàng)建為全局變量拿愧,且正好覆蓋已在全局顯示聲明的變量x
的值,因此認(rèn)為結(jié)果輸出是20碌尔。這很大可能是認(rèn)為輸出為20的小伙伴們的解答浇辜。
在這里我們需要清楚聲明變量和非聲明變量的差異中的兩點(diǎn):
1.聲明變量的作用域限制在其聲明位置的上下文中,而非聲明變量總是全局的唾戚。
2.聲明變量在任何代碼執(zhí)行前創(chuàng)建柳洋,而非聲明變量只有在執(zhí)行賦值操作時(shí)才會(huì)被創(chuàng)建。
不是很清楚上述兩點(diǎn)的小伙伴可以參考JavaScript語(yǔ)句和聲明之var叹坦,里面有很詳細(xì)的講解栗子熊镣。
- 函數(shù)聲明提升
還有部分小伙伴,可能會(huì)認(rèn)為JavaScript引擎是由上而下執(zhí)行代碼募书,當(dāng)執(zhí)行函數(shù)test
時(shí)绪囱,執(zhí)行到第4行函數(shù)內(nèi)部return;
語(yǔ)句使得終止函數(shù)體內(nèi)代碼執(zhí)行跳出函數(shù)體莹捡,覺(jué)得函數(shù)x
沒(méi)任何作用鬼吵。其實(shí),我們要知道的是篮赢,函數(shù)聲明也是會(huì)被提升的而柑,也就是說(shuō)函數(shù)x
的聲明會(huì)被提升到其對(duì)應(yīng)作用域的最頂層文捶。
不懂變量提升和函數(shù)提升的小伙伴可以參看JavaScript:變量提升和函數(shù)提升。
簡(jiǎn)單來(lái)說(shuō)媒咳,就是JS引擎在解析JavaScript代碼之前會(huì)先將代碼進(jìn)行預(yù)編譯粹排,預(yù)編譯期間會(huì)將所有的變量聲明和函數(shù)聲明提升到其對(duì)應(yīng)的作用域的最頂層。且會(huì)先執(zhí)行變量聲明提升涩澡,再執(zhí)行函數(shù)聲明提升顽耳,也就是函數(shù)聲明提升優(yōu)先級(jí)比變量聲明提升優(yōu)先級(jí)高,要注意這個(gè)優(yōu)先級(jí)高是說(shuō)函數(shù)聲明提升要晚妙同,后執(zhí)行射富。關(guān)于變量聲明提升和函數(shù)聲明提升優(yōu)先級(jí)問(wèn)題,在本文最后再作討論驗(yàn)證粥帚。
有了以上知識(shí)點(diǎn)儲(chǔ)備胰耗,可以隱式地將題目代碼理解為:
var x; //變量x聲明提升
function test(){ //函數(shù)test聲明提升
function x() { //函數(shù)x聲明提升
console.log(x);
}
x = 20;
return;
}
x = 10;
test();
console.log(x);
有了上述預(yù)解析后代碼,小伙伴們是否覺(jué)得答案呼之欲出呢芒涡?如果仍然覺(jué)得結(jié)果輸出為20柴灯,則大家忽略了文章關(guān)鍵點(diǎn):變量名與函數(shù)名重名!
不過(guò)费尽,在這之前大家還需要理解作用域鏈的知識(shí)點(diǎn)赠群,這里就簡(jiǎn)單介紹下:
- 作用域鏈?zhǔn)嵌鄠€(gè)上下級(jí)關(guān)系的作用域形成的鏈, 它的方向是從下向上的(從內(nèi)到外)
- 查找變量時(shí),就是沿著作用域鏈來(lái)查找的
簡(jiǎn)單地說(shuō)旱幼,就是嵌套的作用域產(chǎn)生的有內(nèi)向外(由下向上)的作用域鏈查描,用于查找變量的。
那么柏卤,理解作用域鏈的小伙伴知道冬三,在查找一個(gè)變量時(shí),沿著作用域鏈由內(nèi)向外查找缘缚,如果在當(dāng)前作用域查找到對(duì)應(yīng)變量长豁,就直接返回,否則就往其上一級(jí)作用域進(jìn)行查找忙灼;如果在上一級(jí)作用域內(nèi)查找到就直接返回,否則就繼續(xù)向上一級(jí)作用域查找钝侠;直到全局作用域该园,如果還未找到就拋出ReferenceError
異常。
接著帅韧,我們繼續(xù)分析上述預(yù)解析后代碼:
1.首先里初,聲明變量x
,系統(tǒng)在棧內(nèi)存中為變量x分配內(nèi)存空間忽舟,初始化賦值為undefined
双妨;
2.再聲明函數(shù)test
淮阐,系統(tǒng)在棧內(nèi)存中為函數(shù)名test
分配內(nèi)存空間,值為堆內(nèi)存中創(chuàng)建的函數(shù)對(duì)象的內(nèi)存地址值刁品;
3.執(zhí)行賦值語(yǔ)句x = 10
泣特,將變量x
賦值為10;
4.執(zhí)行函數(shù)test
挑随,函數(shù)test
內(nèi)部也有函數(shù)聲明且函數(shù)名為x
状您,緊接著函數(shù)x
定義下有同名變量x
賦值語(yǔ)句x = 20;
,這時(shí)就需要用到前面講的作用域鏈知識(shí)點(diǎn):
????1).在執(zhí)行函數(shù)test
時(shí)兜挨,函數(shù)體內(nèi)部并未調(diào)用執(zhí)行函數(shù)函數(shù)x
膏孟,故函數(shù)x
內(nèi)的代碼并不會(huì)被執(zhí)行;
????2).執(zhí)行函數(shù)test
內(nèi)x = 20;
時(shí)拌汇,按著作用域鏈由內(nèi)向外查找變量x
柒桑,先在當(dāng)前函數(shù)test
作用域內(nèi)查找,當(dāng)前函數(shù)作用域內(nèi)已聲明同名函數(shù)x
的函數(shù)對(duì)象(重名T胍ā)魁淳,因此找到同名函數(shù)x
后,就不再查找傅联;
????3).賦值語(yǔ)句x = 20;
實(shí)際上是給函數(shù)名為x
的函數(shù)對(duì)象重新賦值為20先改。
5.經(jīng)過(guò)前面分析可知,賦值語(yǔ)句x = 20;
是給函數(shù)test
作用域內(nèi)的函數(shù)對(duì)象x
賦值為20蒸走,所以最后在全局作用域內(nèi)執(zhí)行console.log(x);
仇奶,訪問(wèn)的是全局變量x
,且全局變量x
值為10比驻,故此最終輸出結(jié)果為10该溯。
到此為止,小伙伴們應(yīng)該清楚為什么這道題輸出結(jié)果是10别惦,而不是20的整個(gè)分析過(guò)程了哈狈茉!
為了驗(yàn)證大家是否有真正明白其中涉及到的知識(shí)點(diǎn),下面將題目稍作改動(dòng)如下掸掸,輸出結(jié)果又是多少呢氯庆?
var x = 10;
function test(){
x = 20;
return;
}
test();
console.log(x);
相信大家有了上述分析的基礎(chǔ),題目答案一目了然為20呢扰付!那么堤撵,為什么呢?簡(jiǎn)單解析下就是在執(zhí)行函數(shù)test
內(nèi)x = 20;
時(shí)羽莺,沿著作用域鏈由內(nèi)向外查找變量x
实昨,在當(dāng)前函數(shù)作用域內(nèi)未找到變量x
,則會(huì)向上一級(jí)作用域查找盐固,而上一級(jí)作用域就是全局作用域荒给,在全局作用域內(nèi)找到已聲明的變量x
丈挟,故給全局變量x
賦值為20。
接著志电,再次將代碼作改動(dòng)如下:
function test(){
x = 20;
return;
}
test();
console.log(x);
那么曙咽,這個(gè)輸出結(jié)果又是多少呢?相信大家會(huì)不約而同地回答20溪北。對(duì)桐绒,沒(méi)錯(cuò),就是20之拨。為啥呢茉继?很簡(jiǎn)單,在函數(shù)test
內(nèi)部有未聲明變量x
進(jìn)行賦值操作后蚀乔,會(huì)被隱式地創(chuàng)建為全局變量烁竭,所以在全局作用域內(nèi)執(zhí)行console.log(x);
,輸出結(jié)果毫無(wú)疑問(wèn)當(dāng)然就是20呢吉挣!
至此派撕,這道重名問(wèn)題的題也算卸下帷幕呢!接下來(lái)睬魂,就文章前面提到的變量聲明提升和函數(shù)聲明提升優(yōu)先級(jí)問(wèn)題來(lái)驗(yàn)證下终吼。
function bar() {}
var bar;
console.log(bar,typeof bar); //? bar() {} "function"
var bar;
function bar() {}
console.log(bar,typeof bar); //? bar() {} "function"
運(yùn)行結(jié)果均為? bar() {} "function"
,這也就驗(yàn)證了前面說(shuō)到的函數(shù)聲明提升優(yōu)先級(jí)比變量聲明提升優(yōu)先級(jí)高氯哮,而且际跪,再次強(qiáng)調(diào)下,需要注意喉钢,這個(gè)優(yōu)先級(jí)高是說(shuō)函數(shù)聲明提升要晚姆打,后執(zhí)行!3λ洹幔戏!好多人會(huì)誤理解為優(yōu)先級(jí)高就是先執(zhí)行,導(dǎo)致網(wǎng)上好多解釋都是云里霧里的税课。
如果你覺(jué)得這篇文章對(duì)你有幫助啸如,請(qǐng)點(diǎn)贊支持一下哦!