JavaScript闭包是函数与其定义时词法环境绑定的组合,需满足函数嵌套、内部函数引用外部局部变量、外部函数返回内部函数三个条件,用于封装私有数据、保存状态优化性能、解决循环索引问题,但需注意内存泄漏风险。

JavaScript 闭包是函数与其定义时所处的词法环境绑定在一起形成的组合。简单说,就是一个内部函数能记住并访问它创建时所在外部函数的变量,哪怕外部函数已经执行结束。
闭包是怎么形成的
必须同时满足三个条件:
- 存在函数嵌套:内部函数写在外部函数里面
- 内部函数引用了外部函数的局部变量(不是全局变量)
- 外部函数返回了这个内部函数,或者把它作为参数传出去、赋值给变量
一旦满足,外部函数执行完后,它的变量不会被回收,而是被内部函数“锁住”,持续可用。
封装私有数据
这是最常用也最直观的用途。通过闭包,可以把变量藏在函数内部,只暴露特定方法去操作它,避免污染全局或被随意修改。
立即学习“Java免费学习笔记(深入)”;
比如计数器:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
let count = 0;
return {
increment: () => ++count,
get: () => count
};
};
const counter = createCounter();
counter.increment(); // 1
console.log(counter.get()); // 1
console.log(counter.count); // undefined(拿不到)
保存状态做性能优化
防抖(debounce)和节流(throttle)都依赖闭包来记住定时器 ID 或上次执行时间。
例如防抖函数:
function debounce(fn, delay) {let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
这里的 timer 就靠闭包一直存在,每次调用都会重置,确保高频事件只触发最后一次。
循环中绑定正确索引
在 for 循环里给多个元素加事件监听时,容易出现所有回调都取到同一个 i 值的问题。闭包可以解决:
for (let i = 0; i setTimeout(() => console.log(i), 100); // 输出 0,1,2(let 本身有块级作用域)}
// 如果用 var,就得靠闭包:
for (var i = 0; i (function(index) {
setTimeout(() => console.log(index), 100);
})(i); // 输出 0,1,2
本质是把当前 i 的值“捕获”进立即执行函数的作用域,形成独立闭包。
注意内存管理
闭包会让变量长期驻留内存,如果引用了大对象又没及时清理,可能造成内存泄漏。
- 长期存在的定时器、事件监听器、全局变量持有闭包,要特别小心
- 不再需要时,手动解除引用,比如把闭包内变量设为 null
- 优先用 let/const 替代 var,减少显式闭包需求

















