跳至主要内容

請說明 closure 閉包是什麼?如何應用?

閉包是什麼?

closure 函式可以存取其外部函式的變數,即使外部函式已經執行完畢並離開其作用域。可以存取到外部變數的原因在於: JavaScript 使用了語法作用域(lexical scope),在函式定義時會記住函式被定義時的變數環境,並且這些變數會一直存在於記憶體中,只要閉包中的內部函式仍然在使用它們。

當外部函式執行完畢後,通常會釋放其作用域中的變數以節省記憶體。但如果某個內部函式(閉包)仍然引用這些變數,那麼這些變數將不會被釋放,而是持續存在於內部函式的作用域鏈中。

function outerFunction() {
let outerVariable = "I am outside!";

function innerFunction() {
console.log(outerVariable);
}

return innerFunction;
}

const closureFunction = outerFunction();
closureFunction(); // I am outside!
  • 內部函式 innerFunction 是閉包,它記住了 outerFunction 的語法作用域,它可以存取到外部變數 outerVariable,即使 outerFunction 已經執行完畢。

閉包的應用

資料封裝

閉包可以用來模擬私有變數,使得外部無法直接存取某些變數,只能透過內部函式進行操作。

function createCounter() {
let count = 0;

return function () {
count++;
console.log(count);
};
}

const counter = createCounter();
counter(); // 1
counter(); // 2

count 只能透過 counter 函式來來增加或減少值,外部無法直接存取。

模擬私有方法和變數

閉包可以用來模擬類似於物件中的私有變數和方法。

function counter() {
let count = 0;

function increment() {
count++;
}

function decrement() {
count--;
}

function getCount() {
return count;
}

return {
increment,
decrement,
getCount,
};
}
const teamACounter = counter();
teamACounter.increment();
teamACounter.increment();
teamACounter.decrement();
console.log(teamACounter.getCount()); // 1
const teamBCounter = counter();
teamBCounter.increment();
teamBCounter.increment();
console.log(teamBCounter.getCount()); // 2

在這個例子中,count 變數是私有的,外部無法直接存取,只能透過 incrementdecrementgetCount 這三個方法來操作。 同時可以透過閉包的特性,創建多個獨立的 Counter,互不影響。

事件處理(Event handlers)和回呼函式(Callbacks)

閉包經常應用於事件處理或回呼函式中,特別是當需要保持一些狀態或變數時。 React 中的 useState 就是應用了閉包的特性。

import React, { useState } from "react";

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

const handleClick = () => {
setCount((prevCount) => prevCount + 1);
console.log(`Button clicked ${count} times`);
};

return <button onClick={handleClick}>Click me</button>;
};

在這個例子中,handleClick 函式是閉包,每次點擊按鈕都會增加 count 變數的值。


實例練習

99. closure

let dev = "bfe";

function a() {
let dev = "BFE";
return function () {
console.log(dev);
};
}

dev = "bigfrontend";

a()();

解題

答案是"BFE",因為 a() 函式是閉包,它記住了 a 函式被定義時的變數環境,即 dev 為 "BFE"。