JavaScript 核心觀念(61) - ES6 章節:Let 及 Const - Let 有沒有 Hoisting?暫時性死區介紹
前言
接下來這章節將會說明 let
是否有 Hoisting 的問題。
暫時性死區介紹
在很前面的 JavaScript 核心觀念(6)-執行環境與作用域-提升 章節我們有提過提升的問題:
1 | console.log(myName); // undefined |
這邊也算是簡單的複習一下非常前面的章節,否則看到現在可能都已經忘記了。
基本上 Hoisting 會區分為創造階段與執行階段:
1 | // 創造階段 |
而上面的範例是基於 var
來宣告變數的情況,那麼 let
呢?let
也會有相同問題嗎?你可以試著將程式碼修改成以下:
1 | console.log(myName); // ? |
相信你輸入以上程式碼的時候應該要出現 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 | // Chrome: |
但是如果是 const 的話,錯誤訊息就沒有太大差異:
1 | // Chrome: |
那麼我們明白了瀏覽器會更新錯誤訊息之後,那我們怎麼知道它有沒有提升呢?其實我們可以翻一下 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 | // 創造階段 |
這邊要注意 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 | console.log(typeof(a)); // undefined |
因此這邊就可以得到一個結論是「在變數初始化的中間過程,是會生成一個 TDZ 暫時性死區的」。