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
變數是私有的,外部無法直接存取,只能透過 increment
、decrement
和 getCount
這三個方法來操作。
同時可以透過閉包的特性,創建多個獨立的 Counter,互不影響。
閉包經常應用於事件處理或回呼函式中,特別是當需要保持一些狀態或變數時。
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
變數的值。
let dev = "bfe";
function a() {
let dev = "BFE";
return function () {
console.log(dev);
};
}
dev = "bigfrontend";
a()();
答案是"BFE"
,因為 a()
函式是閉包,它記住了 a
函式被定義時的變數環境,即 dev
為 "BFE"。