提升
我們偶而會看到函式先被執行,後面才看到函式本身的定義,
語法上是沒有錯的,但自己想要這樣寫的時候好像就會出現一點小雷?
程式碼的運作順序
JavaScript 雖然是逐行執行的,但在正式執行之前還會經過一些手續,
分為創造階段(creation phase)與執行階段(execution phase),
提升(hoisting)就是在創造階段發生的。
提升是為變數預留記憶體空間,等到要執行時才賦值。
賦值不會提升
我通常以「賦值不會提升」來濃縮整個運作順序,反過來說「宣告是會提升的」。
提升的順序依序是:
- 函式宣告(函式陳述式)
- 引數(arguments)
- 變數宣告(函式表達式)
函式 宣告的優先度最高,
所以閱讀程式碼時才會看到先執行函式後才看到定義的狀況:
sayHello(); // 先執行
function sayHello() {
// 這邊才定義
console.log("Hello!");
}
有時這種先執行後宣告的方式會不成功,大部分是因為誤用函式表達式:
sayHello();
var sayHello = () => {
console.log("Hello!");
};
// 這段程式碼會報錯
// 用 var 宣告會得到 sayHello is not a function
// 用 let 或 const 宣告會得到 Uncaught ReferenceError: sayHello is not defined
這裡是把函式賦值給變數名稱 sayHello 的變數宣告,
而變數宣告在創造階段時還沒有賦值,也就是前面所說的「宣告會提升,賦值不會」,
此時 sayHello 會是一個內容為 undefined 的變數,當然也就無法執行它了!
TDZ
上面的範例會發現 const 與 let 得到的報錯與 var 不一樣,
這是因為 ES6 後的新語法有更完善的撰寫規範與報錯提示,
const 與 let 雖然都會提升,但是撰寫時會禁止在宣告之前有任何存取的行為,
否則將會跳出「xxx is not defined」的報錯。
在變數宣告之前的程式碼會被稱作 TDZ(Temporal Dead Zone):
console.log(a); // Uncaught ReferenceError: a is not defined
const a = 123; // 到這行宣告之前都是 TDZ