在前端開發中,作用域鏈是 JavaScript 引擎用來解析變數識別符號的關鍵機制。它決定了程式碼在執行過程中如何查詢變數。可以將作用域鏈理解為一個有序的列表,其中包含了當前執行環境以及其祖先環境中所有可訪問的變數物件。
以下是關於作用域鏈的一些關鍵理解:
-
作用域: JavaScript 中,作用域定義了變數的可訪問性和生命週期。每個函式都有自己的作用域,全域性程式碼也擁有一個全域性作用域。 ES6 引入了塊級作用域 (用
let
和const
宣告的變數),進一步細化了作用域的概念。 -
作用域鏈的形成: 當程式碼執行時,JavaScript 引擎會建立一個新的執行環境。每個執行環境都有一個與之關聯的變數物件,用於儲存該環境中定義的變數和函式宣告。作用域鏈就是由這些變數物件構成的。
一個函式的作用域鏈在其建立時就已經確定,幷包含以下內容:
- 函式自身的作用域 (區域性作用域): 包含函式的引數、區域性變數和函式宣告。
- 外層函式的作用域: 如果該函式巢狀在另一個函式內部,則作用域鏈會包含外層函式的作用域,以此類推。
- 全域性作用域: 作用域鏈的頂端始終是全域性作用域,包含全域性變數和函式。
-
變數查詢: 當程式碼引用一個變數時,JavaScript 引擎會沿著作用域鏈從當前執行環境開始逐級向上查詢。 如果在當前作用域的變數物件中找到了該變數,則使用該變數;否則,繼續向上查詢,直到找到該變數或到達全域性作用域為止。 如果在整個作用域鏈中都找不到該變數,則會丟擲
ReferenceError
。 -
閉包: 閉包是理解作用域鏈的關鍵概念。閉包是指一個函式能夠訪問其詞法作用域外的變數,即使在其外部函式已經執行完畢後仍然有效。這是因為閉包函式的[[Scopes]] 內部屬性引用了包含其外部函式變數物件的 LexicalEnvironment,使得這些變數得以保留。
-
作用域鏈與
this
的區別: 作用域鏈用於變數查詢,而this
指的是函式執行時的上下文物件,它們是不同的概念。this
的值取決於函式的呼叫方式。
示例:
function outer() {
let a = 1;
function inner() {
let b = 2;
console.log(a); // 1 (在 outer 的作用域中找到 a)
console.log(b); // 2 (在 inner 的作用域中找到 b)
}
inner();
}
outer();
在這個例子中,當 inner()
執行時,它的作用域鏈包含:
inner()
的區域性作用域 (包含b
)outer()
的區域性作用域 (包含a
)- 全域性作用域
理解作用域鏈對於編寫正確和可維護的 JavaScript 程式碼至關重要,它有助於理解變數的訪問規則,避免命名衝突,以及有效地利用閉包。