JavaScript代碼在執(zhí)行時(shí)并不完全是由上到下一行一行執(zhí)行的,由此產(chǎn)生了一個(gè)提升的問(wèn)題。
什么是提升
可以簡(jiǎn)單理解為:聲明(變量和函數(shù))都會(huì)被“移動(dòng)”到各自作用域的最頂端,這個(gè)過(guò)程被稱為提升。
具體例子看提升
下面兩個(gè)例子a會(huì)log什么出來(lái)呢?
a = 233;
var a;
console.log(a)
console.log(a);
var a = 233;
“看起來(lái)”第一個(gè)例子應(yīng)該log出undefined,第二個(gè)例子變量a在使用前沒(méi)有先進(jìn)行聲明,因此會(huì)拋出ReferenceError異常。但實(shí)際上,第一個(gè)例子a輸出233,第二個(gè)例子a輸出undefined。
為什么?
在知道為什么之前有必要了解:JavaScript雖然是解釋型語(yǔ)言,但在解釋JavaScript代碼之前首先對(duì)其進(jìn)行編譯的。編譯階段中的一部分工作就是找到所有的聲明,并用合適的作用域?qū)⑺鼈冴P(guān)聯(lián)起來(lái)。
變量和函數(shù)在內(nèi)的所有聲明都會(huì)在任何代碼被執(zhí)行前首先被處理。
var a = 233;
上面的聲明看起來(lái)是一個(gè)聲明,而對(duì)于JavaScript而言實(shí)際上這是兩個(gè)聲明:var a;和a = 233;。第一個(gè)定義聲明是在編譯階段進(jìn)行的。第二個(gè)賦值聲明會(huì)被留在原地等待執(zhí)行階段。
看回前面兩個(gè)例子,第一個(gè)例子的代碼會(huì)被這樣處理:
var a;
a = 233;
console.log(a);
第二個(gè)例子:
var a;
console.log(a);
a = 233;
注意:只有聲明本身會(huì)被提升,而賦值或其他運(yùn)行邏輯會(huì)留在原地 。
函數(shù)聲明與函數(shù)表達(dá)式
第一個(gè)例子:
foo();
function foo() {
console.log(a); // undefined
var a = 233;
}
函數(shù)foo可以正常執(zhí)行。
第二個(gè)例子:
foo(); // TypeError
var foo = function bar() {
// ...
}
報(bào)錯(cuò):Uncaught TypeError: foo is not a function
由此可以看到:函數(shù)聲明會(huì)被提升,但是函數(shù)表達(dá)式卻不會(huì)被提升。
第三個(gè)例子:
foo(); // TypeError
bar(); // ReferenceError
var foo = function bar() {
// ...
};
這段代碼經(jīng)過(guò)提升后,可以理解為以下形式:
var foo;
foo(); // TypeError
bar(); // ReferenceError
foo = function() {
var bar = ...self...
// ...
};
由此可見:具名的函數(shù)表達(dá)式,名稱標(biāo)識(shí)符在賦值之前也無(wú)法在所在作用域中使用。
函數(shù)優(yōu)先
既然函數(shù)聲明和變量聲明都會(huì)被提升,那么問(wèn)題來(lái)了,重復(fù)聲明的代碼中的優(yōu)先級(jí)是怎樣的,是函數(shù)聲明被提升還是變量聲明被提升?
繼續(xù)驗(yàn)證:
foo(); // 1
var foo;
function foo() {
console.log( 1 );
}
foo = function() {
console.log( 2 );
}
var foo盡管出現(xiàn)在 function foo()...的聲明之前,但它是重復(fù)的聲明而被忽略,因?yàn)?strong>函數(shù)聲明會(huì)被提升到普通變量之前。可以得知:函數(shù)聲明提升的優(yōu)先權(quán)大于普通變量聲明。
盡管重復(fù)的var聲明會(huì)被忽略掉,但出現(xiàn)在后面的函數(shù)聲明還是可以覆蓋前面的:
foo(); // 3
function foo() {
console.log( 1 );
}
var foo = function() {
console.log( 2 );
};
function foo() {
console.log( 3 );
}
ES6中的變量聲明
ES6新增了let、const聲明,它們會(huì)存在一個(gè)暫時(shí)性死區(qū)(TDZ),表現(xiàn)出的情況會(huì)有所不同,具體可以參考:let, const
總結(jié)
- var變量聲明和函數(shù)聲明存在提升
- 函數(shù)表達(dá)式不會(huì)被提升
- 函數(shù)聲明提升的優(yōu)先權(quán)大于普通變量聲明
- let, const由于存在暫時(shí)性死區(qū),表現(xiàn)出的情況和var聲明有所不同