[Note] JS: Scope & Scope Chain


Posted by urlun0404 on 2022-12-04

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):

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

Scope chain的優點在於,若取用變數的所在範疇並無要存取的變數,JavaScript會以「向上查找(varaible lookup)」的方式,透過scope chain往外部範疇尋找是否有該變數,若有則存取,若無則會報錯。

就像 fun2console.log 函式想要存取const變數 c,但從圖上的scope chain可以清楚看出 fun2 沒辦法存取變數 c,所以程式這時就會報錯


References
The Complete JavaScript Course 2023: From Zero to Expert!


#frontend #javascript







Related Posts

簡明 Linux Shell Script 入門教學

簡明 Linux Shell Script 入門教學

C# List相關應用

C# List相關應用

[ MTR04 ] W4_NET101_ 由上到下:從 HTTP 協定開始講起

[ MTR04 ] W4_NET101_ 由上到下:從 HTTP 協定開始講起


Comments