關(guān)于變量提升,大家可能都聽(tīng)的耳朵都起繭了。但是最近的一次討論 ,又讓我有了新的認(rèn)識(shí)。
先看看題:請(qǐng)說(shuō)出1,2對(duì)應(yīng)序號(hào)的結(jié)果
alert(a) 1
a(); 2
function a(){
alert(10)
}
var a=3;
很明顯由于變量提升,在執(zhí)行alert(a)之前,function a和變量a已經(jīng)被初始化了,所以alert(a)其實(shí)是有值的,而不是is not defined。
讓我們來(lái)看看答案:
-
f a(){alert(10)}
關(guān)于這個(gè)答案,當(dāng)時(shí)產(chǎn)生的第一個(gè)疑問(wèn)就是,同樣命名,為什么提升之后a反而是function a(){alert(10)},明明是函數(shù)a先提升的,變量a后提升的,為什么打印出來(lái)的不是undefined?曾經(jīng)在網(wǎng)上看到這么一句話(huà),函數(shù)優(yōu)先級(jí)要高于變量?jī)?yōu)先級(jí),相同命名,變量會(huì)被覆蓋。不知道背后的邏輯究竟是怎么樣的。看了https://zhuanlan.zhihu.com/p/28140450這篇文章之后恍然大悟。
對(duì)于函數(shù)聲明來(lái)講,像如下代碼console.log(a) function a(){ console.log('helloworld') }創(chuàng)建,初始化,賦值的過(guò)程如下:
- 找到所有用 function 聲明的變量,在環(huán)境中「創(chuàng)建」這些變量。
- 將這些變量「初始化」并「賦值」為 function(){ console.log(2) }。
- 開(kāi)始執(zhí)行代碼 fn2()
所以,對(duì)于函數(shù)聲明來(lái)講,在代碼執(zhí)行之前,JS引擎就會(huì)對(duì)其進(jìn)行創(chuàng)建,初始化,賦值
函數(shù)表達(dá)式就只有創(chuàng)建和初始化過(guò)程。
對(duì)于變量表達(dá)式/變量聲明來(lái)講,像如下代碼
console.log(a) var a = 2;- 找到所有用 var 聲明的變量,在環(huán)境中「創(chuàng)建」這些變量。「創(chuàng)建」這些變量(即a)。
- 將這些變量「初始化」為 undefined。所以上面打印出來(lái)的結(jié)果應(yīng)該是undefined。
- 執(zhí)行代碼
- a = 2 將 a 變量「賦值」為 2
- 也就是說(shuō) var 聲明會(huì)在代碼執(zhí)行之前就將「創(chuàng)建變量,并將其初始化為 undefined」。
這樣還是解釋不了我的疑惑,為什么明明是函數(shù)a先提升的,變量a后提升的,為什么打印出來(lái)的不是undefined?找了很多資料似乎也就那句話(huà)
函數(shù)優(yōu)先級(jí)要高于變量?jī)?yōu)先級(jí),相同命名,變量會(huì)被覆蓋理解不了啊!而且我覺(jué)得這句話(huà)有誤導(dǎo)的嫌疑!
上文提到過(guò)var 聲明會(huì)在代碼執(zhí)行之前就將「創(chuàng)建變量,并將其初始化為 undefined」。但是,如果發(fā)現(xiàn)已經(jīng)這個(gè)變量在前面賦值了,就不會(huì)有初始化這一步為undefined這一步,看代碼:function a(){console.log('hello')}; var a; console.log(a)//? a(){console.log('hello')} function a(){console.log('hello')}; var a = undefined; console.log(a)//undefined所以上述過(guò)程應(yīng)該這樣子的:
進(jìn)行變量提升,function a()被提升到作用域的最前面,變量a也進(jìn)行提升了,但是它發(fā)現(xiàn)已經(jīng)有a了,所以沒(méi)有了初始化a為undefined這一步。所以在alert(a)的時(shí)候,依舊是
? a(){alert('10')} 10 //執(zhí)行了a(){alert(10)}
不知道你看懂了嗎????
條件判斷下的函數(shù)提升
上面既然我們已經(jīng)說(shuō)到,對(duì)于函數(shù)聲明(不是函數(shù)表達(dá)式哦)來(lái)講,在代碼執(zhí)行之前,JS引擎就會(huì)對(duì)其進(jìn)行創(chuàng)建,初始化,賦值。但是,遇到條件判斷語(yǔ)句,像下面的:
(function () {
if (false) {
function test() {
console.log(2);
}
}
test();
})();
在早期的瀏覽器中,會(huì)打印出2,因?yàn)閷?duì)于函數(shù)聲明來(lái)講,在代碼執(zhí)行之前,JS引擎就會(huì)對(duì)其進(jìn)行創(chuàng)建,初始化,賦值。所以就算進(jìn)入條件判斷語(yǔ)句,test()函數(shù)已經(jīng)在當(dāng)前作用域創(chuàng)建并賦值了。
但很明顯,這跟我們的出發(fā)點(diǎn)是背離的,我們寫(xiě)的條件判斷語(yǔ)句完全沒(méi)有意義了!
后來(lái)瀏覽器為了修正這個(gè)錯(cuò)誤,像這種情況下面的函數(shù)提升就只是 var test提升到函數(shù)作用域的頂端,本題中false所以沒(méi)進(jìn)入函數(shù)體,所以test()就會(huì)報(bào)錯(cuò)test is not a function。而不是像第一題那樣,整個(gè)函數(shù)體都提到前面。
除了if,好像while,switch,for也一樣。
ecma文檔:http://www.ecma-international.org/ecma-262/6.0/index.html
再來(lái)做做題吧!
解釋一下下面執(zhí)行的結(jié)果
(function(){
console.log(a);
function a(){
console.log(a);
}
var a = 1;
})() //? a(){console.log(a);}
你看懂了嗎??