關(guān)于編譯器,引擎會(huì)在解釋 JS 代碼之前首先對(duì)其進(jìn)行編譯。編譯階段的一部分工作就是找到所有的聲明,并用合適的作用域?qū)⑺鼈冴P(guān)聯(lián)起來(lái)。
1.聲明提升
“提升”就是變量和函數(shù)聲明從它們?cè)诖a中出現(xiàn)的位置被“移動(dòng)”到了最上面,這個(gè)過(guò)程就叫“提升”。
// 代碼示例 1
foo();
function foo() {
console.log(a); // undefined
var a = 2;
}
如上面代碼示例 1, foo 函數(shù)的聲明被提升了,因此第一行中的調(diào)用可以正常執(zhí)行。需要注意的是,每個(gè)作用域都會(huì)進(jìn)行提升操作。也就是函數(shù)自身也會(huì)在內(nèi)部對(duì) var a 進(jìn)行提升,所以最終的形式可以理解為如下代碼示例 2:
// 代碼示例 2
function foo() {
var a;
console.log(a); // undefined
a = 2;
}
foo();
2.函數(shù)表達(dá)式不會(huì)被提升
// 代碼示例 3
foo(); // TypeError 類(lèi)型錯(cuò)誤
var foo = function bar() {
// ...
}
foo() 由于對(duì) undefined 值進(jìn)行函數(shù)調(diào)用而導(dǎo)致非法操作,因此拋 TypeError 出異常。
3.函數(shù)優(yōu)先
函數(shù)聲明會(huì)被提升到普陀變量之前。
// 代碼示例 4
foo(); // 1
var foo;
function foo() {
console.log(1);
}
foo = function() {
console.log(2);
}
上述代碼示例 4 會(huì)被引擎理解為如下形式:
function foo() {
console.log(1);
}
foo(); // 1
foo = function() {
console.log(2);
}
重復(fù)的函數(shù)聲明可以覆蓋前面的。
盡可能避免在塊內(nèi)部聲明函數(shù)。
總結(jié)
-
var a = 2;其中var a;是編譯階段的任務(wù),a = 2;是執(zhí)行階段的任務(wù); - 所有的聲明(變量和函數(shù))都會(huì)被“移動(dòng)”到各自作用域的最頂端,這個(gè)過(guò)程叫“提升”;
- 聲明本身會(huì)被提升,而包含函數(shù)表達(dá)式在內(nèi)的賦值操作不會(huì)被提升;
- 避免重復(fù)聲明。
注:文章參考總結(jié)自 《你不知道的 JavaScript 上卷》[美] KYLE SIMPSON 著 第 42 頁(yè)。