DEV Community

SGsSY
SGsSY

Posted on

The Most Familiar Stranger - JavaScript - 閉包

分享記錄檔 YouTube

根據 MDN 的說法閉包是函式以及該函式被宣告時所在的作用域環境 ( lexical environment ) 的組合

語法作用域 ( lexical scoping )

function init() {
    let name = 'John';
    function getName() {
        return name;
    }
    getName();
}

init();   // John
Enter fullscreen mode Exit fullscreen mode

getName() 並無定義 name 這個變數,但它可以參考語法作用域的變數,因此能取用到父函式宣告的變數 name,如果父函式沒找到,它可以繼續往上找,一路找到全域變數。global 的 lexical scope 就是 null。

閉包 ( Closure )

function init() {
    let name = 'John';
    function getName() {
        return name;
    }

    return getName;
}

let doInit = init();
doInit();   // John
Enter fullscreen mode Exit fullscreen mode
function init() {
    let name = 'John';
    function getName() {
        console.log(name); // 不能在 name 被宣告前使用
        let name = 'Jay';
    }

    return getName;
}
Enter fullscreen mode Exit fullscreen mode

在函數內部捕獲和保存當前語境中的變數和狀態,並將其留存在函數外部,以便在後續的呼叫中繼續使用,即使當前作用域已經被銷毀了。

應用場景

狀態保存

function useState(initialState) {
  let state = initialState;

  function getState() {
    return state;
  }

  function setState(updatedState) {
    state = updatedState;
  }
  return [getState, setState];
}

const [count, setCount] = useState(0);

count();   // 0
setCount(1);
count();   // 1
setCount(500);
count();   // 500
Enter fullscreen mode Exit fullscreen mode

模擬私有變數

function createCounter() {
  let count = 0;
  function counter() {
    count++;
    console.log(count);
  }
  return counter;
}

const counter1 = createCounter();
counter1(); // 1
counter1(); // 2
counter1(); // 3

const counter2 = createCounter();
counter2(); // 1
counter2(); // 2
counter2(); // 3
Enter fullscreen mode Exit fullscreen mode

⚠️ 閉包是很強大的功能,但過度使用容易造成記憶體流失 (memory leak)

  • 以下程式碼會依序輸出什麼呢?

    function closure() { 
        let counter = 0; 
        return () => { 
            counter++;
            console.log(counter);
        }; 
    }
    
    const counter1 = closure();
    const counter2 = closure();
    
    counter1();
    counter1();
    counter2();
    counter1();
    

    答案:1, 2, 1, 3

Top comments (0)