☕ NEW! 完成新手任務即可參加抽獎!LINE 星巴克禮券等你拿,名額有限!        🎉 推廣活動:邀請好友註冊 DevLearn,累積推薦抽 LINE 星巴克禮券! 活動詳情 →        🔥 活動期間 2026/4/1 - 5/31 |已有 0 人參加       
concept-frontend 中級

閉包與記憶體:為什麼會洩漏?

閉包 = 函式 + 它記住的外部變數

function createCounter() {
    let count = 0;  // ← 這個變數被閉包「抓住」了
    return () => ++count;
}

const counter = createCounter();
counter(); // 1
counter(); // 2
counter(); // 3
// count 不會被垃圾回收,因為 counter 還在引用它

為什麼會記憶體洩漏?

陷阱 1:事件監聽器沒移除

function setupButton() {
    const data = new Array(1000000).fill('x'); // 大量資料

    document.getElementById('btn').addEventListener('click', () => {
        console.log(data.length); // 閉包引用了 data
    });
}

setupButton();
// data 永遠不會被回收,因為事件監聽器的閉包引用著它
// 即使你不再需要 data,它也一直佔著記憶體

// ✅ 修復:移除監聽器
const handler = () => console.log('clicked');
btn.addEventListener('click', handler);
// 不需要時
btn.removeEventListener('click', handler);

陷阱 2:定時器沒清除

function startPolling() {
    const hugeData = fetchSomething();

    setInterval(() => {
        process(hugeData); // 閉包抓住 hugeData
    }, 1000);
}
// setInterval 永遠不會停止 → hugeData 永遠不會被回收

// ✅ 修復
const intervalId = setInterval(...);
clearInterval(intervalId); // 不需要時清除

陷阱 3:DOM 引用

function setup() {
    const element = document.getElementById('modal');
    element.addEventListener('click', () => {
        element.style.display = 'none'; // 閉包引用 element
    });
    // 即使 modal 從 DOM 移除,JS 記憶體中還是有引用 → 不會被回收
}

WeakMap / WeakRef

// WeakMap 的 key 是弱引用,不阻止垃圾回收
const cache = new WeakMap();

let obj = { name: '小明' };
cache.set(obj, 'cached data');

obj = null; // obj 被回收 → WeakMap 中的 entry 也自動消失
// 不會造成記憶體洩漏!

如何檢測記憶體洩漏?

Chrome DevTools → Memory 分頁
1. 拍第一張 Heap Snapshot
2. 執行可疑的操作
3. 拍第二張 Heap Snapshot
4. 比較差異 → 看哪些物件增加了

閉包本身不是壞事(它是 JS 的核心特性),問題是忘了清理不再需要的引用。

💡 大家的想法 · 0

載入中...
💬 即時聊天室 🟢 0 人在線
😀 😎 🤓 💻 🎮 🎸 🔥
➕ 新問題
📋 我的工單
💬 LINE 社群
🔒
需要註冊才能使用此功能
註冊帳號即可解鎖測驗、遊戲、簽到、筆記下載等所有功能,完全免費!
免費註冊