初學者指南:深入了解 JavaScript 的建立期與執行期
更新日期: 2024 年 11 月 21 日
本文為 JS 底層運作邏輯系列文,第四篇
在學習 JavaScript 時,理解代碼的執行機制對於寫出穩定、高效的程式碼至關重要。
特別是「建立期」(Creation Phase)和「執行期」(Execution Phase),這兩個階段決定了變數和常數在代碼中的行為。
本文將聚焦於變數的建立期與執行期,幫助新手了解 var
、let
、const
的特性,以及它們在程式執行中的作用。
什麼是建立期與執行期?
每當 JavaScript 引擎執行程式碼時,會為每個執行上下文(Execution Context)創建兩個階段:
- 建立期(Creation Phase):在這個階段,JavaScript 引擎會為變數和函式的聲明進行處理,但不會執行任何代碼。
- 執行期(Execution Phase):在這個階段,JavaScript 引擎才開始逐行執行代碼,為變數賦值,調用函式等。
變數宣告的建立期與執行期
讓我們以一個簡單的變數宣告代碼為例,來說明建立期和執行期各自做了什麼。
代碼示例:
var a;
console.log(a);
建立期
- 變數提升(Hoisting):
- JavaScript 引擎會將使用
var
聲明的變數提升到作用域的頂部。 - 在建立期,變數
a
被創建並初始化為undefined
。
- JavaScript 引擎會將使用
- 初始化的意思是為變數設定一個初始值。
- 在這裡,
a
的初始值是undefined
,表示變數已存在但尚未賦予具體的值。
- 在這裡,
執行期
- 代碼執行:
- 執行
var a;
,但因為已在建立期處理,實際上這行代碼在執行期不做任何事情。 - 執行
console.log(a);
,此時a
的值為undefined
,因此輸出undefined
。
- 執行
var
變數的特性與可能出現的問題
特性
- 變數提升:
var
聲明的變數會在建立期被提升到作用域頂部。 - 全域或函式作用域:
var
變數沒有區塊作用域,只受函式或全域作用域限制。
可能的問題
變數提升導致預期外的 undefined
:
console.log(a); // 輸出:undefined
var a = 10;
- 解釋:
- 在建立期,變數
a
被提升並初始化為undefined
。 - 在執行
console.log(a);
時,a
還未被賦予真正的值,仍然是undefined
。
- 在建立期,變數
- 初始化:
- 在這裡指的是變數已被創建並賦予初始值
undefined
,但尚未被賦值為10
。
- 在這裡指的是變數已被創建並賦予初始值
作用域污染:
var
變數可能在區塊外被訪問或修改,導致意外的行為。
if (true) {
var a = 5;
}
console.log(a); // 輸出:5
- 解釋:
a
在if
區塊內聲明,但因為var
沒有區塊作用域,a
被提升到函式或全域作用域,並初始化為undefined
,最終被賦值為5
。 - 初始化的過程使得
a
在整個作用域中可用,可能導致意外的結果。
let
和 const
變數的特性
let
的特性
- 區塊作用域:
let
變數只在其所在的區塊內有效。 - 不會提升為
undefined
:在建立期,let
變數被創建但不初始化。 - 補充解釋:這意味著在變數真正聲明並初始化之前,無法訪問該變數。這段期間稱為「暫時性死區」。
const
的特性
- 區塊作用域:與
let
一樣,const
也具有區塊作用域。 - 必須初始化:
const
變數在聲明時必須賦值,且值不可重新賦值。 - 補充解釋:
const
變數在建立期被創建,但如果沒有同時進行初始化,會導致語法錯誤。
暫時性死區(Temporal Dead Zone,TDZ)
- 概念:在區塊作用域內,從代碼的開始到變數被聲明並初始化之前的區域,稱為暫時性死區。
- 行為:在 TDZ 內訪問
let
或const
變數會拋出ReferenceError
。
改善了什麼問題?
- 避免變數提升導致的
undefined
問題:因為在 TDZ 內無法訪問未初始化的變數,避免了誤用未賦值的變數。 - 提升代碼安全性:強制開發者在使用變數之前進行聲明和初始化。
代碼示例與解釋
let
變數的建立期與執行期
代碼示例:
let a;
console.log(a);
建立期:
- 變數
a
被創建但不初始化。 - 補充解釋:此時
a
存在於作用域內,但無法被訪問,直到執行期進行初始化。
執行期:
- 執行
let a;
,此時a
被初始化為undefined
。- 補充解釋:在執行
let a;
時,a
才正式可用,並擁有初始值undefined
。
- 補充解釋:在執行
- 執行
console.log(a);
,輸出undefined
。
解釋:
- 雖然
a
在建立期已被創建,但直到執行let a;
時才被初始化。 - 在這之前訪問
a
會導致ReferenceError
,因為變數尚未被初始化。
const
變數的建立期與執行期
代碼示例:
const a;
console.log(a);
建立期:
- 變數
a
被創建但不初始化。 - 補充解釋:
const
變數在建立期被創建,但必須在聲明時進行初始化,否則會產生錯誤。
執行期:
- 執行
const a;
,但因為沒有賦值,會拋出SyntaxError
。 - 補充解釋:
const
變數要求在聲明的同時進行初始化,缺少初始值會導致語法錯誤。
解釋:
const
變數必須在聲明時初始化,否則無法通過語法檢查。
修正代碼:
const a = 10;
console.log(a); // 輸出:10
- 在建立期,
a
被創建但未初始化。 - 在執行期,執行
const a = 10;
,a
被初始化為10
。
var
變數在條件語句中的行為
代碼示例:
if (false) {
var a = 1;
}
console.log(a);
建立期:
- 變數
a
被提升並初始化為undefined
。 - 補充解釋:即使條件為
false
,var
聲明的變數仍然在建立期被創建並初始化。
執行期:
if
條件為false
,所以區塊內的代碼不執行,a
不會被賦值為1
。- 執行
console.log(a);
,輸出undefined
。
解釋:
- 由於
a
已在建立期被初始化為undefined
,但執行期沒有賦予新值,所以輸出undefined
。 - 這可能導致預期外的行為,因為變數
a
存在於全域作用域中,但沒有被賦值。
總結
var
的問題:- 變數提升可能導致預期外的
undefined
,因為變數在建立期已被初始化為undefined
。 - 沒有區塊作用域,可能導致作用域污染。
- 變數提升可能導致預期外的
let
和const
的優勢:- 擁有區塊作用域,變數只在聲明的區塊內有效。
- 暫時性死區(TDZ)防止在變數聲明和初始化前訪問變數,提高代碼的安全性。
- 最佳實踐:
- 儘量使用
let
和const
,避免使用var
。 - 在使用
const
時,必須同時進行聲明和初始化。 - 注意暫時性死區,確保在變數聲明並初始化後再進行訪問。
- 儘量使用
結語
理解 JavaScript 的建立期與執行期,以及不同變數聲明方式的特性,能夠幫助你寫出更可靠、可維護的代碼。
透過認識 var
、let
、const
的差異和適用場景,你可以避免許多常見的錯誤,提升程式的品質。
附錄:代碼執行流程總結
var
變數:
- 建立期:變數被提升並初始化為
undefined
。- 補充解釋:在建立期,變數已存在並擁有初始值
undefined
,可在作用域內任何地方訪問。
- 補充解釋:在建立期,變數已存在並擁有初始值
- 執行期:在代碼中賦值或修改變數。
let
和 const
變數:
- 建立期:變數被創建但不初始化,進入暫時性死區。
- 補充解釋:在建立期,變數已存在於作用域內,但尚未初始化,無法被訪問。
- 執行期:
- 對於
let
,在執行聲明時被初始化為undefined
。 - 對於
const
,在執行聲明時必須同時進行初始化,賦予具體的值。
- 對於
- 暫時性死區(TDZ):
- 從作用域開始到變數聲明並初始化之前的區域。
- 在 TDZ 內訪問變數會拋出
ReferenceError
。
- 補充解釋:TDZ 的存在確保了變數在被正確初始化之前,無法被使用,避免了潛在的錯誤。
希望這篇文章能夠幫助你更好地理解 JavaScript 中的建立期與執行期,以及「初始化」在其中的關鍵作用。
透過深入了解這些概念,你可以在未來的開發中寫出更健壯、可維護的程式碼!