一、閉包是什么?一個(gè)簡(jiǎn)單的例子
function outer() {
let me = '小楊';
return function inner() {
console.log(`大家好,我是${me}`);
};
}
const sayHello = outer();
sayHello();
看到?jīng)]?inner
函數(shù)記住了outer
函數(shù)的me
變量,這就是閉包!
二、閉包的三大妙用(天使面)
1. 創(chuàng)建私有變量
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. 實(shí)現(xiàn)函數(shù)柯里化
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. 內(nèi)存泄漏
function leakMemory() {
const bigData = new Array(1000000).fill('*');
return function() {
console.log('我還記得bigData');
};
}
const leaked = leakMemory();
2. 性能問(wèn)題
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;
}
四、閉包優(yōu)化六大法則(6年經(jīng)驗(yàn)總結(jié))
1. 及時(shí)釋放引用
function createHeavyObject() {
const heavy = new Array(1000000).fill('*');
return {
useHeavy: function() {
},
cleanup: function() {
heavy = null;
}
};
}
2. 使用塊級(jí)作用域
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: '我是私有數(shù)據(jù)'
});
}
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 }
};
})();
五、真實(shí)案例分享
案例1:我曾經(jīng)在項(xiàng)目中遇到一個(gè)頁(yè)面卡頓問(wèn)題,最后發(fā)現(xiàn)是因?yàn)橐粋€(gè)事件處理函數(shù)形成了閉包,持有了一個(gè)大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);
});
}
六、總結(jié)
閉包就像一把雙刃劍:
? 優(yōu)點(diǎn):實(shí)現(xiàn)私有變量、函數(shù)柯里化、模塊化等
? 缺點(diǎn):可能導(dǎo)致內(nèi)存泄漏、性能問(wèn)題
記住我的6年經(jīng)驗(yàn)總結(jié):
- 及時(shí)釋放不再需要的引用
- 優(yōu)先使用塊級(jí)作用域
- 避免不必要的閉包
- 合理使用WeakMap和模塊化
- 善用開發(fā)者工具檢查內(nèi)存
轉(zhuǎn)自https://juejin.cn/post/7512259761196957707
該文章在 2025/6/6 9:18:57 編輯過(guò)