在初學階段,還蠻常碰到明明定義好的變數卻回報 error,可能是因為對 Scope 的觀念沒有理解。
我習慣稱 Scope 為作用域,有人稱為範疇,是一個用來查找、訪問變數及函式的規則。
在 ES6 的 let、const 出現之前,我們只有函式作用域 ( var ),但 ES6 後多了區塊作用域,主要是定義了 let、const 作用範圍 。
簡單來說,作用域表示變數或者函式有作用的地方。
function sayHi(){
var guy = 'Lisa'
function greet(){...}
console.log(guy)
}
console.log(guy)
greet()
上面程式碼,我們在全域宣告了一個函式 sayHi ,函式裡面宣告變數 guy 以及函式 greet,但當我們想從全域取用 guy 跟 greet 就會報錯,因為這兩者的作用域在 sayHi 裡面。
但為什麼下面的例子,我們可以訪問函式以外的變數?
var idol = 'Rose'
function sing(){
console.log(idol)
}
sing()
idol 宣告在全域環境,卻能在 sing 函式裡呼叫,這是靠作用域鏈 Scope Chain 查找的結果。
前幾篇講執行環境 Execution context 的時候,我們提到 JS 遇到函式調用時會創建一個屬於該函式的執行環境,每個函式內部有自己的 [[scope]],調用函式時的執行環境會把 [[scope]] 跟 Scope Chain 連結。
當全域執行環境建立時,創造階段初始化了全域的 variable object 以及 scope chain。
globalExecutionContext:{
VO:{
idol:undefined,
sing:function
},
ScopeChain:{
globalExecutionContext.VO
}
}
// 並且宣告 sing 函式時,sing 的作用域會參考由全域給的 scopechain 的內容
sing.[[scope]] = globalExecutionContext.ScopeChain
接著 sing() 被調用,因著 sing 的 scope 可以透過 scoope chain 取得外部全域的變數
此外,若無使用宣告定義變數的話,則 JS 會視為全域變數 ( 這是不好的習慣哦~ )
var idol = 'Rose'
function sing(){
fruit = 'grape'
}
console.log(fruit)