闭包
以下是关于闭包的详细解析及典型应用场景示例:
一、闭包核心概念
闭包 = 函数 + 其创建时的词法环境
当函数能记住并访问其所在的词法作用域(即使函数在其作用域外执行),就形成了闭包。
二、典型闭包场景
1. 计数器(状态保持)
javascript
function createCounter() {
let count = 0; // 被闭包保护的变量
return {
increment: () => ++count,
getCount: () => count
};
}
const counter = createCounter();
counter.increment(); // 1
counter.getCount(); // 1
闭包特征:increment 和 getCount 函数持续访问父作用域的 count
2. 私有变量(数据封装)
javascript
function createPerson(name) {
let _age = 0; // 私有变量
return {
getName: () => name,
setAge: (age) => _age = age,
getInfo: () => `${name}(${_age})`
};
}
const person = createPerson('Alice');
person.setAge(25);
person.getInfo(); // "Alice(25)"
闭包作用:隐藏实现细节,防止直接修改 _age
3. 模块化开发
javascript
const calculator = (() => {
let memory = 0; // 模块私有状态
return {
add: (a, b) => {
memory = a + b;
return memory;
},
getMemory: () => memory
};
})();
calculator.add(2,3); // 5
calculator.getMemory(); // 5
设计模式:IIFE + 闭包 实现模块化
4. 事件处理(状态捕获)
html
<button id="btn">Click 0</button>
<script>
function setupCounter() {
let count = 0;
const btn = document.getElementById('btn');
btn.addEventListener('click', () => {
btn.textContent = `Click ${++count}`;
});
}
setupCounter();
</script>
闭包价值:事件回调函数持续访问外部作用域的 count
5. 函数柯里化(严格分解为单参数链)
javascript
function multiply(a) {
return (b) => a * b; // 闭包保留a的值
}
const double = multiply(2);
double(5); // 10
应用场景:参数预置、函数组合
6. 循环陷阱与解决方案
- 问题代码:
javascript
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i); // 输出5个5
}, 100);
}
- 闭包解决方案:
javascript
for (let i = 0; i < 5; i++) { // let 块级作用域
setTimeout(() => {
console.log(i); // 0,1,2,3,4
}, 100);
}
// 或使用IIFE
for (var i = 0; i < 5; i++) {
((j) => {
setTimeout(() => console.log(j), 100);
})(i);
}
三、闭包内存管理
特性 | 说明 |
---|---|
内存保留 | 闭包变量不会自动回收 |
释放条件 | 当闭包函数不再被引用时 |
常见泄漏场景 | DOM事件未解绑、全局变量引用 |
四、闭包优缺点分析
- 优点:
- 实现私有变量
- 保持状态持久化
- 支持函数式编程范式
- 缺点:
- 过度使用可能导致内存占用过高
- 不当使用可能引发内存泄漏
- 调试复杂度增加(难以追踪闭包变量)
五、现代JS优化
- WeakMap:替代传统闭包存储大对象
javascript
const privateData = new WeakMap();
function MyClass() {
privateData.set(this, { secret: 42 });
}
通过理解这些典型场景,可以更好地把握闭包在工程实践中的应用边界。