什麼是提升 (Hoisting)?
2023年2月7日
在《在 JavaScript 中用 `var`, `let`, 以及 `const` 有什麼差別?什麼時候該用哪個?》這篇文章中,曾經提到 var、let 與 const 有「提升 (hoisting) 的差別」,而這篇文章會更詳細的回答提升 (hoisting) 究竟是什麼。
什麼是提升 (hoisting) ?
大部分人應該都曾經在寫 JavaScript 程式碼時,在宣告函式之前就使用它,如下方程式碼:
sayHello(); // Hello
function sayHello() {
console.log("Hello");
}
但執行這樣的程式碼並不會報錯,其原因就是因為提升 (Hoisting)。
提升 (Hoisting) 並非 ECMAScript® 2015 Language Specification 中的一個正式定義,但它用來形容 JavaScript 編譯階段將變數和函式的宣告存入記憶體的概念。
這個特性會使函式和變量的宣告被提升到作用域的頂部,即使他們的實際定義位置在下面。但要注意,JavaScript 引擎並不會將程式碼實際移到頂部,而只是這個概念的形容。
變數與函式的提升
var 提升 (hoisting)
var 的提升 (hoisting) 是指在編譯階段,JavaScript 引擎會將所有的 var 變數宣告提升到該函式作用域的頂端。雖然變數宣告被提升了,但並不會賦值,如下方程式碼,提早呼叫 name 的結果會是 undefined 而非 Tom。
console.log(name); // undefined
var name = "Tom";
let 提升 (hoisting)
許多人會誤以為 let 不會提升 (hoisting),因為我們如果是在宣告 let 前就使用,會出現以下錯誤。
console.log(greeting); // Uncaught ReferenceError: greeting is not defined
let greeting = "hi there";
上方的程式碼之所以會拋出錯誤,並不是因為 let 沒有 hoisting。雖然提升 (hoisting) 並沒有被定義在標準規範中的名詞解釋,但概念上,let、const 跟 var 同樣會有提升 (hoisting) 的行為,不過其中有以下差異:
var會提升到函式作用域 (function scope),但let和const只會提升到區塊作用域 (block scope)var在創建變數與定義變數範圍時,會同時將變數值自動初始化為undefined; 但當let在提升變數到區塊作用域 (block scope) 範圍時,並不會初始化此變數,這個狀態可以稱之為 uninitialized,也有另一個常見的說法是,let和const定義的變數目前存在於暫時死區 (TDZ,Temporal dead zone)。
函式提升
函式宣告也有提升,與 var 提升的差異為,函式提升也會創建好函式物件,因此可以在宣告前呼叫。
foo(); // 1
function foo() {
console.log(1);
}
但函式提升要注意的是,如果是函式表達式,提升行為會與其宣告的變數一樣,如下方程式碼,用 var 宣告的 foo 函式,在宣告前使用時,當時值會是 undefined,因此呼叫 undefined 會報錯。
foo(); // Uncaught TypeError: foo is not a function
var foo = function () {};
用 let 宣告的 foo 函式,在宣告前使用時,此時 foo 在暫時死區,因此呼叫 foo 會報錯。
foo(); // Uncaught ReferenceError: foo is not defined
let foo = function () {};
為什麼有暫時死區 (TDZ, Temporal dead zone)錯誤?
許多文章中提到,暫時死區(TDZ, Temporal dead zone) 出現好處是可以避免我們在變數在還沒有被宣告前就能拿來使用,但其實,還有另外一個最主要的設計概念。
You Don't Know JS 的作者在曾經在這門課中提出給出很好的解釋:暫時死區 (TDZ, Temporal dead zone) 錯誤其實是為了 const 所設計的。試想一下,如果 const 的提升行為與 var 相同,因此我們在宣告前訪問到 const 變量時,會拿到 undefined 的值,但我們也知道 const 是常數,同個作用域中值不應該變動,因此如果先拿到 undefined 後再拿到不同值的設計會不符合規範。因此,設計了暫時死區的錯誤,避免這種情況發生。