
JavaScript 語言核心(13)在 Scope chain 查找變數
JavaScript 語言核心(12)Closure 與一級函式 << 前情 在 你的變數 var 了嗎? 中談過變數範圍,當時說過,使用 在 Closure 與一級函式 中則以置閒變數觀念,從語法層面說明 Closure 的意義與作用,也可應付絕大多數的情況。 事實上,JavaScript 在查找變數時,會循著 Scope chain 逐一查找,Scope chain 可說明為何 JavaScript 沒有區塊範圍,也是 JavaScript 中閉包的實現機制。 就結論而言,你可沿著 Scope chain 來查找變數,也就是看看函式自身的 context 物件上是否有該特性,如果沒有就往外頭的 context 物件看看有沒有該特性。 如果要稍微深入一下 JavaScript 的 Scope chain,首先得了解一些名詞。首先是 Lexical Scope,這代表著 JavaScript 原始碼的物理位置(Physical placement)。例如: var x = 10; function outer() { var y = 20; function inner() { var z = 30; } } func(); 在結構上,函式 像是上例中, 在 Rhino 直譯器中,可以使用函式上「非標準」的 以上例來說, 當你試圖在 Node.js 不提供非標準方式來取得函式運行時包裹該函式的 context 物件,若想驗證一下 Scope chain,你可以試著取得 Rhino 直譯器,並依以下進行驗證,從中理解 Scope chain 的運作。例如: js> function outer() { > var y = 20; > function inner() { > var z = 30; > } > return inner; > } js> var f = outer(); js> outer.__parent__ == this; true js> f.__parent__.__parent__ == this; true js> f.__parent__.y; 20 js> f.__parent__.__parent__.x; 10 js> JavaScript 的變數,其實就是 context 物件上的特性。以上例而言,你可以透過 查找變數就是依序在 context 物件上尋找特性,可以說明以下這個例子: js> function func() { > print(m); > var m = 10; > print(m); > } js> func(); undefined 10 js> 逐步來看的話,就是這樣: function func() { print(m); // func 的 context 物件上沒有 m 特性(也就是 undefined) var m = 10; // func 的 context 物件有 m 特性且值為 10 print(m); // func 的 context 物件有 m 特性且值為 10 } 若使用非標準的 js> function func() { > function inner() {} > print(inner.__parent__.m); > var m = 10; > print(inner.__parent__.m); > } js> func(); undefined 10 js> JavaScript 在查找變數時,會先在目前 context 物件上找,若找不到指定名稱,則會到外層 context 物件上找,若找不到,就再到更外層 context 物件找,直到全域物件為止,這樣的順序形成變數查找的 Scope chain。 再來看區域變數覆蓋全域變數的例子: var x = 10; function func() { var x = 20; print(x); } 以 Scope chain 來解釋的話,在 js> var x = 10; js> function func() { > function inner() {} > var x = 20; > print(inner.__parent__.x); > } js> func(); 20 js> 再來看 Closure 的例子: function doSome() { var x = 10; function f(y) { return x + y; } return f; } 在內部的 如果你了解 Scope chain,結合非標準 js> function func() { > function inner() {} > inner.__parent__.y = 30; > print(y); > } js> func(); 30 js> 即使 可以這麼說,在 JavaScript 中,所有的變數,都是某個物件上的特性。 附帶一提的是,自行使用 js> var x = 10; js> function func() { > var x = 20; > var f = new Function('return x;'); > print(f.__parent__.x); > return f(); > }; js> func(); 10 10 js> |