[JS] 提昇(Hoisting)機制

如果今天想要使用電腦,該怎麼做?

你的答案很可能是,按下電腦開機鍵就好,但實際上得先有主機、滑鼠、鍵盤、螢幕、電源、作業系統…等軟、硬體環境,才有辦法使用電腦。

JavaScript 雖然是直譯式語言(不需透過編譯器轉譯成可執行檔),但是『執行』之前,還是需要先把原本字元組成的程式碼編譯成執行環境(就像是上面提到的準備滑鼠、鍵盤…等等),而其中一個步驟就是先將 變數函數 的名稱放入記憶體,這種行為在 ECMA 的文件中沒有明確定義專有名詞,一般會用 提昇(Hoisting)來稱呼。

參考文章:
MDN - 提升(Hoisting)


提昇(Hoisting)的方式會因為變數類型有所不同:

💎 var、function:

按照逐行執行的邏輯,下面前兩行程式碼會因為還沒有產生這個變數而出現錯誤,但實際上並不會出現任何錯誤。

1
2
3
4
5
6
7
8
console.log(a); // undefined
console.log(foo); // foo(){return 'Hello';}

var a;

function foo() {
return 'Hello';
}

從結果回推實際執行的順序會像下面這樣:

1
2
3
4
5
6
7
8
var a;

function foo() {
return 'Hello';
}

console.log(a);
console.log(foo);

這次做小調整,改成賦予 a 變數一個值

1
2
3
console.log(a); // undefined
var a = 1;
console.log(a); // 1

雖然沒有出現錯誤,但是 a 的值並沒有跟著被提昇,回推的執行順序會像下面這樣:

1
2
3
4
var a
console.log(a); // undefined
a = 1;
console.log(a); // 1

再看下面這段程式碼,想想看結果是什麼:

1
2
3
var b = 1;
var b;
console.log(b);

如果不清楚運作 JavaScript 的運作機制,可能會認為答案是 undefined,覺得後者蓋掉前者,但答案其實是 1,回推後的實際執行會像是下面這樣:

1
2
3
4
var b;
var b;
b = 1;
console.log(b);

『重複宣告』加上『提昇機制』是不是讓人覺得很坑呢?

💎 let、const:

ES6 版本提供了 letconst 兩種變數宣告方式,這兩種新的方式解決了 var 的許多奇怪問題。

使用 letconst 宣告的變數名稱在 JavaScript 從『環境建立』一直執行到『變數實際存在的那一行程式碼』的這段期間,變數名稱都是無法被取用的,這段期間稱為 Temporal Dead Zone(TDZ),暫時性死區。

這個機制就像是我買了對號座的票(宣告),這個位置的票就不能被其他人購買(不能重複宣告),但是在我坐上車位前(實際執行的那段程式碼),都無法在這個位置找到我(TDZ鎖定)。

以下是使用 letconst 宣告的輸出結果:

1
2
3
console.log(x); // x is not defined
let x = 1;
console.log(x); // 前面已經因為執行階段出錯終止,不會執行到這裡

參考文章
1、我知道你懂 hoisting,可是你了解到多深?
2、理解ES6中的暫時死區(TDZ)