iT邦幫忙

0

javascript 變數的生命週期 (Variable Lifetime)、作用域(Scope)、作用域查找鏈(Scope Chain)

  • 分享至 

  • xImage
  •  

先說結論。作用域及作用域查找鍊,前者是指宣告變數的使用範圍(邊界)涵蓋哪裡。後者是指在程式碼區塊內,使用一個不在同區塊內宣告的變數,因為在當前範圍找不到值 會由內往外找的機制。
變數的生命週期則是執行程式離開了宣告變數的作用域(區塊)後,變數會消失。

作用域(Scope)

使用var、let/const宣告變數後的使用範圍。

變數的生命週期 (Variable Lifetime)

當程式執行離開了該宣告變數的作用域的邊界後,在該作用域內宣告的變數會被系統回收 (Garbage Collected),實際上就是「消失」了。這種現象在程式設計中稱為 「變數生命週期結束 (Variable Lifetime Ends)」。
這也是為什麼用let 和 const 宣告變數比用 var 宣告變數更有優勢。let/const只要程式執行離開 {} 區塊,變數就可以被立即標記回收。這減少了記憶體的佔用。而用var宣告變數,變數要等到整個函式執行結束,才能被標記回收,記憶體的佔用時間就變得更長。

var

使用var宣告某個變數 這個變數能使用(能存取到的值) 會在離它最近的 function {} 大括號。它忽略 if 或 for 的 {}。
函式作用域 (Function Scope)

var:函式作用域範例

javascript

function testVar() {
    // 變數 y 只能在 testVar 函式內部存取
    
    if (true) {
        var y = 10; 
        console.log("A. 內部:", y); // 輸出 A. 內部: 10
    }
    
    // 雖然離開了 if 的 {} 區塊,但因為是 var,它仍在函式作用域內
    console.log("B. 外部:", y); // 輸出 B. 外部: 10
}

testVar();

 console.log("C. 全域:", y);  ❌ 報錯:函式外部無法存取函式內部的 var

console.log("A. 內部:", y); 會輸出 >> A. 內部: 10,這部分應該沒問題,因為這個var宣告的y就在function testVar的{}裡面。
console.log("B. 外部:", y); 會輸出 >> B. 外部: 10,這部分也能理解,因為這個var宣告範圍是在function testVar的{}裡面,而這行也是在函式testVar的{}裡面,一樣能找到這個y。
最後一行 console.log("C. 全域:", y); 因為在當前作用域找不到變數y,因為宣告var這個變數y是在函式裡面才能使用,所以會出現會找不到而出現錯誤 >> ReferenceError: y is not defined。

let/const

邊界是 離它最近的 {} 大括號。包含函式、if、for、甚至單純的 {}。
區塊作用域 (Block Scope)

let:區塊作用域範例

javascript

// 👈 第 3 層:全域作用域 開始

function testLet() {
    // 👈 第 2 層:函式作用域 開始
    
    let x = 5; 
    
    if (true) {
        // 區塊作用域 開始 (if 區塊)
        let x = 10; 
        console.log("C. 內部:", x); // 輸出 10
    }
    // 區塊作用域 結束 (內層 x 銷毀)
    
    console.log("D. 外部:", x); // 輸出 5 

} 
// 函式作用域 結束 (x = 5 在這裡被銷毀!)

testLet(); 

console.log("E. 全域:", x); // ❌ ReferenceError! (x 已在上面的 } 結束時被銷毀)

console.log("C. 內部:", x); 會輸出 >> C. 內部: 10,這部分應該沒問題,因為這個let宣告的x就在if的{}裡面。
console.log("D. 外部:", x); 會輸出 >> D. 外部: 5,這部分應該也可理解,因為在同區塊有使用let宣告 x = 5。
console.log("E. 全域:", x); 會輸出 >> ReferenceError: x is not defined。因為此區塊已經最外層,也就是所謂的全域作用域,但在此層並沒有找到x,所以會出錯。

const:區塊作用域範例

javascript

function testConst() {
    const PI = 3.14; // 這裡的 PI
    
    if (true) {
        const PI = 3.14159; // 內層 PI (新變數,遮蔽)
        console.log("F. 內部 PI:", PI); // 輸出 F. 內部 PI: 3.14159
        
        // PI = 10; // ❌ 報錯:TypeError (不可對 const 重新賦值)
    }
    
    console.log("G. 外部 PI:", PI); // 輸出 G. 外部 PI: 3.14 (外層 PI 的值未變)
}

testConst();

console.log("F. 內部 PI:", PI); // 會輸出 >> F. 內部 PI: 3.14159,這部分應該沒問題。
console.log("G. 外部 PI:", PI); // 會輸出 >> G. 外部 PI: 3.14 (外層 PI 的值未變)。

作用域查找鏈(Scope Chain)

作用域查找鏈範例:

javascript

const A = 100; // 👈 第 3 層:全域變數

function outerFn() {
    const B = 50; // 👈 第 2 層:外部函式變數

    function innerFn() {
        const C = 20; // 👈 第 1 層:內部函式變數

        // 1. 嘗試查找 A (未在 innerFn 宣告)
        // 查找鏈啟動:內層 -> 外層 -> 全域
        console.log("H. A:", A); // 輸出 H. A: 100 (找到全域的 A)

        // 2. 嘗試查找 B (未在 innerFn 宣告)
        // 查找鏈啟動:內層 -> 外層
        console.log("I. B:", B); // 輸出 I. B: 50 (找到 outerFn 的 B)

        // 3. 嘗試查找 C (已在 innerFn 宣告)
        console.log("J. C:", C); // 輸出 J. C: 20 (直接使用內部的 C)

        // 4. 嘗試查找 X (沒有在任何一層宣告)
        // console.log("K. X:", X); // ❌ 報錯:ReferenceError (查找鏈到底,找不到)
    }
    
    innerFn();
}

outerFn();

console.log("H. A:", A); // 輸出 H. A: 100。可以看出在區塊(function innerFn)裡沒有A,所以往外層找,沒有找到就再往外層找,找到A為100。
console.log("I. B:", B); // 輸出 I. B: 50。所在區塊(function innerFn)裡沒有B,所以往外層找,找到B為50。
console.log("J. C:", C); // 輸出 J. C: 20。所在區塊(function innerFn)裡有C=20。
console.log("K. X:", X); // 輸出 ReferenceError :X is not defined。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言