Day 32:JavaScript 執行環境與作用域機制

概述

JavaScript 引擎透過解析器(Parser)將原始碼轉換為抽象語法樹(AST),再經由直譯器或即時編譯器(JIT Compiler)轉換為可執行程式碼 。現代引擎如 V8 採用混合策略,結合直譯與編譯的優勢提升執行效能。

執行環境類型

JavaScript 定義三種執行環境(Execution Context):

全域執行環境(Global Execution Context)

預設的執行環境,程式啟動時自動建立 :

  • 建立全域物件(瀏覽器環境為 window)

  • 建立 this 變數並指向全域物件

  • 進行記憶體配置

函式執行環境(Function Execution Context)

僅在函式被呼叫時產生 :

  • 建立 arguments 物件,存放傳入的引數資訊

  • 每個函式執行環境彼此獨立

  • 可同時存在多個函式執行環境

Eval 執行環境

由 eval() 函式產生,用於執行字串形式的程式碼,但因安全性與效能考量已較少使用 。[7]

執行環境堆疊

JavaScript 採用「後進先出」(LIFO)的堆疊結構管理執行環境 :

function firstFunc() {
  console.log('First');
}

function secondFunc() {
  firstFunc();
  console.log('Second');
}

secondFunc();

執行順序:

  1. 全域執行環境入堆疊

  2. secondFunc() 執行環境入堆疊

  3. firstFunc() 執行環境入堆疊

  4. firstFunc() 執行完畢出堆疊

  5. secondFunc() 執行完畢出堆疊

  6. 全域執行環境最後出堆疊

作用域機制

語彙環境(Lexical Environment)

程式碼的撰寫位置決定變數的存取範圍,這是 JavaScript 的靜態作用域特性 。

作用域定義

變數可被存取的有效範圍 :

  • 全域作用域:在程式任何位置都可存取

  • 函式作用域:僅限函式內部存取(var 宣告)

  • 區塊作用域:僅限 {} 區塊內存取(let/const 宣告)

// 全域作用域
const globalVar = 'global';

function example() {
  // 函式作用域
  var functionVar = 'function';
  
  if (true) {
    // 區塊作用域
    let blockVar = 'block';
    const blockConst = 'const';
    console.log(blockVar); // 'block'
  }
  
  console.log(blockVar); // ReferenceError
}

作用域鏈(Scope Chain)

當變數在當前作用域找不到時,會沿著作用域鏈向外層查找,直到全域作用域為止 。

提升機制(Hoisting)

執行環境的兩個階段

執行環境以兩個階段運作 :

創建階段(Creation Phase)

對變數和函式進行記憶體配置 :

  • var 宣告的變數初始化為 undefined

  • 函式宣告完整存入記憶體

  • let/const 配置記憶體空間但不初始化

執行階段(Execution Phase)

逐行執行程式碼並賦值 。

var 的提升行為

console.log(x); // undefined(不會報錯)
var x = 5;
console.log(x); // 5

等同於:

var x; // 創建階段:記憶體配置並初始化為 undefined
console.log(x);
x = 5; // 執行階段:賦值
console.log(x);

let 與 const 的提升行為

let 和 const 的宣告會被提升並配置記憶體,但不會初始化為 undefined 。從區塊開始到宣告語句執行前,這段區間稱為「暫時性死區」(Temporal Dead Zone, TDZ),嘗試存取會拋出 ReferenceError :

console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 10;
{
  // TDZ 開始
  console.log(z); // ReferenceError
  // TDZ 持續
  let z = 20; // TDZ 結束
  console.log(z); // 20
}

函式提升

函式宣告會完整提升,可在宣告前呼叫 :

greet(); // 'Hello'

function greet() {
  console.log('Hello');
}

最佳實踐

根據 Airbnb JavaScript Style Guide :

  • 優先使用 const,需要重新賦值時使用 let

  • 避免使用 var

  • 在作用域頂部宣告變數,避免依賴提升機制

  • 使用區塊作用域增強程式碼可讀性與可維護性

參考資源