作用域(Scope)
變數的地盤有多大、能生效的範圍有多廣,一切取決變數誕生的地方與宣告的方式。這個範圍就稱為這個變數的作用域(Scope)。
作用域的類型分為兩種:
動態作用域(dynamic scope):以呼叫函式的地方當作作用域鏈。
靜態作用域(static scope)、詞法作用域(lexical scope):以宣告函式的地方當作作用域鏈。
JavaScript 所採用的是靜態作用域。
在 Javascript 中,作用域分為三個等級:
Function Level Scope
函式內宣告的變數。
宣告位置:function 內。
有效範圍:該 function 之內。
說明:
在 Function 內,從宣告開始到最後都有效。
在 Function 外就變成未定義。
1 | function myFunc(){ |
Global Level Scope
每個執行 JavaScript 程式的環境,會有一個全域物件 (Global Object)。全域物件裡的變數,無論在哪裡宣告,效力都能遍及整個程式,我們稱為全域變數 (Global Variables)。
HTML:全域物件是 window object。
Node.js:全域物件是 global object。
宣告位置:主程式內,任何 Function 之外。
有效範圍:整個程式,包含所有 Function 內。
說明:
無論 Function 內外都能使用。
呼叫方式可以是直接呼叫變數名稱,或透過全域物件 window 去呼叫。
1 | function myFunc(){ |
ps.Local 內的變數有可能自動轉成 Global 變數。
在 JavaScript 裡有一種狀況會自動產生全域變數,那就是賦值給未宣告的變數。
1 | function myFunc(){ |
若是 Global 和 Function 內宣告了同樣名稱的變數,在 Function 內會是 Local 變數生效,Function 外依然是以 Global 為主。如果在 Function 內指定用 Global 變數(window.xx)的話,以 Global 為主。
1 | function myFunc(){ |
Block Level Scope (ES6 let、const)
Block 指的是一段大括號{}
包起來的區塊。
宣告位置:Block 內。
有效範圍:該 Block 之內。
說明:
在 Block 內,從宣告開始到最後都有效。
在 Block 外就變成未定義。
1 | { |
範圍鏈、或稱作用鏈(Scope Chain)
當在該執行環境中的物件找不到變數時,就會透過作用鏈的機制來向宣告函式的外層尋找。
e.g.
1 | var a = 1; |
函式 y 是在全域執行環境下被初始化的,因此函式 y 的 scope chain 除了自己本身之外,另一個就是找到全域執行環境當中,因此輸出 1。
1 | var a = 1; |
函式 y 是在函式執行環境 x 下被初始化的,但本身沒有 a 變數,因此向外尋找,從 y() –> x() –> global environment 查找 a 變數的過程,就是 Scope chain。在此範例中,因在 x() 就找到 a = 2,所以不再往外尋找。
1 | var a = 1; |
函式 y 是在全域執行環境下被初始化的,因此函式 y 的 scope chain 除了自己本身之外,另一個就是找到全域執行環境當中的 x,執行 x() 時,內部的 a 去更動了全域環境的 a 值,因此輸出 2。
參考:
https://ithelp.ithome.com.tw/articles/10203387
https://shawnlin0201.github.io/JavaScript/JavaScript-Scope/#more
https://pjchender.blogspot.com/2015/12/javascriptscope-chainouter-environment.html