一、什么是提升
a = 2;
var a;
console.log(a);
上面的代碼輸出的是否是undefined?
答案是否定的,執(zhí)行完畢我們會發(fā)現(xiàn)打印出a的值為2,這個似乎和代碼自上而下執(zhí)行的結(jié)果相違背,代碼執(zhí)行的過程中到底發(fā)生了什么?
要明白發(fā)生了什么首先要解釋下js程序的執(zhí)行過程:
js通常被認(rèn)為是動態(tài)語言或解釋語言,但是事實(shí)上它是一門編譯語言,只不過它不是像java等提前編譯,而是在執(zhí)行代碼前的幾微秒瞬間編譯。
編譯的過程和傳統(tǒng)編譯編譯語言非常相似:

js簡單編譯過程示意圖.png
編譯階段的一部分工作就是找到所有的變量聲明并將它們綁定在對應(yīng)的作用域中(函數(shù)作用域或塊作用域)。
因此,正確的思考思路是,包括變量和函數(shù)在內(nèi)的所有聲明都會在任何代碼被執(zhí)行前首先被處理。所以上面代碼的正確執(zhí)行過程是:
var a;
a = 2;
console.log(a);
以上這種聲明提前的現(xiàn)象就是提升。
二、提升示例
1. 函數(shù)聲明會提升,函數(shù)表達(dá)式不會提升
foo();
bar();
var foo = function bar() {
}
上述代碼聲明提升后如下:
var foo;
foo()//TypeError: foo is not a function
bar()//ReferenceError: bar is not defined
foo = function () {
var bar = self
}
只有聲明會提升,表達(dá)式不會提升,所以foo聲明被提前,此時foo為undefined,進(jìn)行函數(shù)調(diào)用會發(fā)生TypeError錯誤,bar不會提升,此時會報錯ReferenceError。
2. 函數(shù)和變量都會提升,函數(shù)提升會在變量提升之前
foo();
var foo;
function foo() {
console.log(1)
}
foo = function () {
console.log(2);
}
輸出的結(jié)果是1,進(jìn)行函數(shù)提升后:
function foo() {
console.log(1)
}
foo();
foo = function () {
console.log(2);
}
由此也說明定義相同名稱的變量是一件很糟糕的事情。
總結(jié):
- 只有聲明本身會被提升,而賦值或其他運(yùn)行邏輯會留在原地。如果提升改變了代碼執(zhí)行的順序,會造成非常嚴(yán)重的破壞。
- 函數(shù)聲明會被提升,但是函數(shù)表達(dá)式卻不會被提升。
- 函數(shù)聲明和變量聲明都會提升,但是函數(shù)聲明會提升到變量聲明之前。要避免重復(fù)聲明。
參考文檔:《你不知道的JavaScript(上卷)》