你懂 JavaScript 嗎?#14 動態範疇(Dynamic Scope)

你所不知道的 JS

本文主要是比較動態範疇與語彙範疇的差異。

動態範疇(Dynamic Scope)vs 語彙範疇(Lexical Scope)

前情提要,先前提過範疇是指編譯器或 JavaScript 引擎藉由識別字名稱(identifier name)查找變數的一組規則;而「語彙範疇」是指在語彙分析時期所定義的靜態範疇,且不經由 eval 或 with 在執行時的修改。語彙範疇考量的是變數「如何和何處被宣告」,其查找的範疇串鏈是以程式碼的巢狀結構為基礎。

範例如下,在 foo 中的 a 由於無法在所在函式 foo 內經由 RHS 解析得到 a 的值,因此會往全域範疇查找,因而得到 3。

function foo() {
  console.log(a); // 3
}

function bar() {
  var a = 2;
  foo();
}

var a = 3;
bar();

相較之下,「動態範疇」是指在執行時期(runtime)動態地決定範疇,考量點是變數「何處被呼叫」,其查找的範疇串鏈是以呼叫堆疊(call stack)為基礎。

範例如下,同樣的, a 在 foo 中無法解析其值,因此循著 call stack 找到 foo 被呼叫的地方 bar,在 bar 內找到 a 的值為 2。

function foo() {
  console.log(a); // 2
}

function bar() {
  var a = 2;
  foo();
}

var a = 3;
bar();

事實上,JavaScript 並沒有動態範疇,但 this 的查找規則是類似動態範疇的,在後續關於 this 的篇章會再詳細說明。

回顧

看完這篇文章,我們到底有什麼收穫呢?藉由本文可以理解到…

語彙範疇與動態範疇的主要差異在於,前者查找的範疇串鏈是以程式碼的巢狀結構為基礎,而後者則是以呼叫堆疊(call stack)為基礎。

References


同步發表於2019 鐵人賽


You-Dont-Know-JS javascript 你所不知道的JS 2019鐵人賽 你懂JavaScript嗎? 鐵人賽 You-Dont-Know-JS-Scope-and-Closures 你懂 JavaScript 嗎?2019 iT 邦幫忙鐵人賽 系列文