一.閉包的定義
閉包是指函數和其周圍的詞法環境的引用的組合。
簡單來說,就是函數可以記住并訪問其在定義時的作用域內的變量,即使該函數在其它作用域調用。
也就是說,閉包讓你可以在一個內層函數中訪問到其外層函數的作用域。
function createCounter() {let count = 0; // 私有變量return {increment() {return ++count;},getCount() {return count;}};
}const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.getCount()); // 2
- 函數攜帶著它定義時的作用域信息
二.閉包的原理(底層機制)
在 JS 中,每次函數執行都會創建一個執行上下文棧(Execution Context),上下文包含變量環境(Variable Environment)。當內部函數被返回或傳出外部時,JavaScript 引擎會將相關變量的引用保留在內存中,不會立即銷毀,從而形成閉包。
三.閉包的應用場景
①:數據封裝 / 私有變量
function counter() {let count = 0;return {increment: () => ++count,decrement: () => --count,get: () => count};
}let c = counter();
console.log(c.increment()); // 1
console.log(c.get()); // 1
②:函數工廠
function multiply(x) {return function(y) {return x * y;};
}const multiplyByTwo = multiply(2);
console.log(multiplyByTwo(4)); // 8
③:事件處理
<template><button id="btn1">按鈕1</button><button id="btn2">按鈕2</button>
</template><script setup>
import { onMounted } from 'vue';function handleClick(message) {return function() {console.log(message);};
}//等待組件掛載完
onMounted(() => {document.getElementById("btn1")?.addEventListener('click', handleClick('按鈕1被點擊了'));document.getElementById("btn2")?.addEventListener('click', handleClick('按鈕2被點擊了'));
});</script>
④:模塊化實現
使用閉包創建私有變量和方法,形成模塊化代碼結構
const module = (function() {let privateVar = 'private';function privateMethod() {return privateVar;}return {publicMethod() {return privateMethod();}};
})();
⑤:循環中的閉包
// 錯誤示例for (var i = 0; i < 3; i++) {setTimeout(() => console.log(i), 0); // 3,3,3}// 正確示例// 使用let和const替代varfor (let i = 0; i < 3; i++) {setTimeout(() => console.log(i), 0); // 0,1,2}// 正確示例// 使用閉包for (var i = 0; i < 3; i++) {(function(j) {setTimeout(function() {console.log(j); // 0 1 2}, 100);})(i);}
四.閉包的缺點
-
容易造成內存泄漏(引用的變量得不到釋放)。
-
調試難度增大(變量狀態隱藏在函數中)。
注意內存管理
// 內存泄漏示例
function leakMemory() {const largeData = new Array(1000000);return function() {console.log(largeData[0]);}
}// 避免內存泄漏
function avoidLeak() {const data = process(largeData);return function() {console.log(data); // 只保留需要的數據}
}
五、閉包與垃圾回收
通常,函數執行完后其作用域就會被銷毀。但閉包中被引用的變量會被保留在內存中,只要閉包仍在使用,變量就不會被回收。
這可能造成內存問題,尤其是在網頁中綁定大量 DOM 事件但未解綁的情況下。