Scope(範疇、範圍)是一個非常重要的主題,一般提到scope是在討論變數可被存取的範圍,不過scope有很多類似的名詞,以下先一一釐清:
- scoping: 指的是以什麼方式決定變數被存取的範圍(How)
- lexical scoping: 即是「以變數所在的位置」決定變數存取的範圍
- scope: 指變數被宣告的空間(space)、環境(environment)或地方(place),可分為全域範疇(global scope)、函式範疇(function scope)、區塊範疇(block scope)
- scope of variable: 指的是一個變數可被存取的"整個"範圍。
Scope
下圖分別說明何謂全域範疇(global scope)、函式範疇(function scope)和區塊範疇(block scope):
有幾個比較需要注意的重點:
- var變數沒有區塊範疇(block scope);
- 在ES2015之後,strict mode的JavaScript程式碼視函式範疇為區塊範疇。
這裡以下方程式碼來說明不同變數所在範疇的存取限制:
- 變數 a 是全域範疇變數,所以在任何地方(如
fun2
)都可以被存取。 - 變數 b 是函式範疇變數,在ES2015以後嚴格模式下也是區塊範疇變數,所以只能在
fun1
內被存取,同樣的限制也可套用於fun2
的變數 e 和fun3
的變數 f。 - 須留意,
fun2
無法存取const變數 c,但可以存取var變數 d,即是因為const變數有block scope,存取權會被外層的if
區塊所限制;而var變數沒有block scope,但只能在fun1
的範疇內存取。
const a = 'global a';
fun1();
function fun1(){
let b = "fun1 b";
consoel.log('function 1 calls '+ b);
if(true){
const c = "if-block c";
var d = "fun1 d";
}
function fun2(){
const e = "fun2 e"
fun3();
console.log(
'function 2 calls '+ a + ', '+ c ', '+ d + ', '+ e // c 不能存取
);
}
fun2();
}
function fun3(){
const f = "fun3 f";
console.log('function 3 calls'+ f);
}
Scope Chain
在介紹建立執行環境(execution context)的同時也會建立scope chain(範疇鏈),可以視作儲存所在範疇以外的父母層變數。
Scope chain的功能在於任何變數都可以存取自身範疇以外的所有父母層範疇,白話地來說,任何變數都能存取所在位置以外的父母層變數。
這裡將前面程式碼簡單繪製出scope chain:
Scope chain的優點在於,若取用變數的所在範疇並無要存取的變數,JavaScript會以「向上查找(varaible lookup)」的方式,透過scope chain往外部範疇尋找是否有該變數,若有則存取,若無則會報錯。
就像 fun2
的 console.log
函式想要存取const變數 c,但從圖上的scope chain可以清楚看出 fun2
沒辦法存取變數 c,所以程式這時就會報錯
References
The Complete JavaScript Course 2023: From Zero to Expert!