一、閉包是什么?一個簡單的例子
function outer() {
let me = '小楊';
return function inner() {
console.log(`大家好,我是${me}`);
};
}
const sayHello = outer();
sayHello();
看到沒?inner
函數記住了outer
函數的me
變量,這就是閉包!
二、閉包的三大妙用(天使面)
1. 創建私有變量
function createCounter() {
let count = 0;
return {
increment() { count++ },
getCount() { return count }
};
}
const counter = createCounter();
counter.increment();
console.log(counter.getCount());
console.log(counter.count);
2. 實現函數柯里化
function multiply(a) {
return function(b) {
return a * b;
};
}
const double = multiply(2);
console.log(double(5));
3. 事件處理中的妙用
function setupButtons() {
for(var i = 1; i <= 3; i++) {
(function(index) {
document.getElementById(`btn-${index}`)
.addEventListener('click', function() {
console.log(`我是按鈕${index}`);
});
})(i);
}
}
三、閉包的三大坑(魔鬼面)
1. 內存泄漏
function leakMemory() {
const bigData = new Array(1000000).fill('*');
return function() {
console.log('我還記得bigData');
};
}
const leaked = leakMemory();
2. 性能問題
function slowPerformance() {
const data = {};
return function(key, value) {
data[key] = value;
};
}
3. 意外的變量共享
function createFunctions() {
let funcs = [];
for(var i = 0; i < 3; i++) {
funcs.push(function() {
console.log(i);
});
}
return funcs;
}
四、閉包優化六大法則(6年經驗總結)
1. 及時釋放引用
function createHeavyObject() {
const heavy = new Array(1000000).fill('*');
return {
useHeavy: function() {
},
cleanup: function() {
heavy = null;
}
};
}
2. 使用塊級作用域
function createFixedFunctions() {
let funcs = [];
for(let i = 0; i < 3; i++) {
funcs.push(function() {
console.log(i);
});
}
return funcs;
}
3. 避免不必要的閉包
function unneededClosure() {
const data = {};
return function() {
console.log('Hello');
};
}
function noClosure() {
console.log('Hello');
}
4. 使用WeakMap管理私有變量
const privateData = new WeakMap();
class MyClass {
constructor() {
privateData.set(this, {
secret: '我是私有數據'
});
}
getSecret() {
return privateData.get(this).secret;
}
}
5. 合理使用IIFE
(function() {
const tempData = processData();
})();
6. 使用模塊化
const counterModule = (function() {
let count = 0;
return {
increment() { count++ },
getCount() { return count }
};
})();
五、真實案例分享
案例1:我曾經在項目中遇到一個頁面卡頓問題,最后發現是因為一個事件處理函數形成了閉包,持有了一個大DOM樹的引用。解決方案是:
function setup() {
const bigElement = document.getElementById('big');
button.addEventListener('click', function() {
console.log(bigElement.id);
});
}
function setup() {
const id = 'big';
button.addEventListener('click', function() {
console.log(id);
});
}
六、總結
閉包就像一把雙刃劍:
? 優點:實現私有變量、函數柯里化、模塊化等
? 缺點:可能導致內存泄漏、性能問題
記住我的6年經驗總結:
- 及時釋放不再需要的引用
- 優先使用塊級作用域
- 避免不必要的閉包
- 合理使用WeakMap和模塊化
- 善用開發者工具檢查內存
轉自https://juejin.cn/post/7512259761196957707
該文章在 2025/6/6 9:18:57 編輯過