JavaScript 核心觀念(61) - ES6 章節:Let 及 Const - Let 有沒有 Hoisting?暫時性死區介紹

前言

接下來這章節將會說明 let 是否有 Hoisting 的問題。

暫時性死區介紹

在很前面的 JavaScript 核心觀念(6)-執行環境與作用域-提升 章節我們有提過提升的問題:

1
2
3
console.log(myName); // undefined
var myName = 'Ray';
console.log(myName); // Ray

這邊也算是簡單的複習一下非常前面的章節,否則看到現在可能都已經忘記了。

基本上 Hoisting 會區分為創造階段與執行階段:

1
2
3
4
5
6
// 創造階段
var myName;
// 執行階段
console.log(myName); // undefined
myName = 'Ray';
console.log(myName); // Ray

而上面的範例是基於 var 來宣告變數的情況,那麼 let 呢?let 也會有相同問題嗎?你可以試著將程式碼修改成以下:

1
2
console.log(myName); // ?
let myName = 'Ray';

相信你輸入以上程式碼的時候應該要出現 Uncaught ReferenceError: Cannot access 'myName' brfore initialization.,如果你是在較新版的 Chrome 瀏覽器輸入你會發現實際上出現的是則是會出現 Uncaught ReferenceError: myName is not defined,而 Firefox 目前我測試 89.0.2(64 位元)版本則是 Uncaught ReferenceError: can't access lexical declaration 'myName' before initialization

因此這代表著瀏覽器更新之後是會更新錯誤訊息的,這一點要多加注意一下,雖然我覺得 Chrome 的錯誤訊息比較直覺就是了:

1
2
3
4
5
6
7
// Chrome:
console.log(a); // Uncaught ReferenceError: a is not defined
let a = '1';

// Firefox:
console.log(a); // ReferenceError: can't access lexical declaration `a' before initialization
let a = '1';

但是如果是 const 的話,錯誤訊息就沒有太大差異:

1
2
3
4
5
6
7
// Chrome:
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
const a = '1';

// Firefox:
console.log(a); // ReferenceError: can't access lexical declaration `a' before initialization
const a = '1';

那麼我們明白了瀏覽器會更新錯誤訊息之後,那我們怎麼知道它有沒有提升呢?其實我們可以翻一下 MDN 文件,有一句話是關鍵:

The variable is in a “temporal dead zone” from the start of the block until the initialization is processed.

在變數完成初始化之前呼叫的話,是會處於暫時性死區 (TDZ),這也就是為什麼會出現 ReferenceError 錯誤,而實際上 let 運作模式有變嗎?其實並沒有,只是會變成 ReferenceError 暫時性死區錯誤:

1
2
3
4
5
6
7
// 創造階段
let myName;

// 執行階段
console.log(myName); // Uncaught ReferenceError: a is not defined
myName = 'Ray';
console.log(myName);

這邊要注意 let 會在 myName = 'Ray'; 之前建立一個暫時性死區的階段,所以你在 myName = 'Ray'; 勢必都會出現 ReferenceError 錯誤的。

那麼我們該怎麼驗證 let 有提升呢?首先一般來說如果你想要 console.log 一個不存在的變數是會得到 Uncaught ReferenceError: xxx is not defined 的,例如:

1
console.log(a); // Uncaught ReferenceError: a is not defined

但是如果你使用 console.log(typeof(a)) 則是會出現 undefined,因此我們可以這樣子驗證:

1
2
3
console.log(typeof(a)); // undefined
console.log(typeof(myName)); // Uncaught ReferenceError: myName is not defined
let myName = 'Ray';

因此這邊就可以得到一個結論是「在變數初始化的中間過程,是會生成一個 TDZ 暫時性死區的」。

參考文獻

Liker 讚賞

這篇文章如果對你有幫助,你可以花 30 秒登入 LikeCoin 並點擊下方拍手按鈕(最多五下)免費支持與牡蠣鼓勵我。
或者你可以也可以請我「喝一杯咖啡(Donate)」。

Buy Me A Coffee Buy Me A Coffee

Google AD

撰寫一篇文章其實真的很花時間,如果你願意「關閉 Adblock (廣告阻擋器)」來支持我的話,我會非常感謝你 ヽ(・∀・)ノ