闭包通过内部函数持续引用外部变量,使词法环境不被销毁,变量绑定在作用域链中而非复制;每次调用外层函数生成独立闭包,变量生命周期取决于内部函数可访问时长。

闭包能保存局部变量不被销毁,本质是因为内部函数持续引用了外部函数的变量,使 JavaScript 引擎无法通过垃圾回收机制释放这些变量——只要引用链存在,变量就常驻内存。
闭包保存变量的核心机制
函数执行结束后,其执行上下文本该被销毁,局部变量也随之清除。但若函数内部定义的另一个函数(如返回的函数或传给异步操作的回调)仍访问该函数的局部变量,引擎就会保留这个变量所在的词法环境,形成闭包。
- 变量不是“复制”进闭包,而是被“绑定”在作用域链中
- 每次调用外层函数(如 createCounter()),都会创建独立的作用域和独立的闭包,彼此互不影响
- 闭包变量生命周期 = 内部函数可被访问的时间长度
典型写法:返回函数持有变量
这是最常见也最清晰的闭包模式,用于封装状态:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const inc = createCounter();
console.log(inc()); // 1
console.log(inc()); // 2
这里 count 没有被销毁,是因为返回的匿名函数持续引用它;每次调用 createCounter() 都生成新闭包,inc 和另一个 inc2 的 count 完全隔离。
其他常见场景中的闭包变量保留
闭包不仅出现在显式返回函数时,也活跃于异步、事件监听、定时器等场景:
- 循环中 setTimeout + var:所有回调共享同一个 i,最终都输出最后值(如全为 5)——这是闭包“保留了变量”,但保留的是被反复修改的同一份引用
- 循环中 setTimeout + let:每次迭代生成独立绑定,每个回调闭包持有各自的 i 副本(准确说是独立绑定),所以输出 0~4
-
事件处理器中捕获索引或 ID:如
btns[i].onclick = () => console.log(i),若用 var i 会出错,改用 let i 或 IIFE 即可让每个处理器记住自己的 i
如何主动释放闭包占用的内存
闭包变量长期驻留内存可能引发内存泄漏,尤其在 DOM 节点或大型对象被闭包引用时:
- 将引用闭包的变量设为 null,切断作用域链,例如:
inc = null; - 移除不再需要的事件监听器(尤其用匿名函数绑定时,应改用具名函数并用 removeEventListener 清理)
- 避免在全局作用域中长期持有闭包函数,尤其是它引用了大量数据或 DOM 元素时
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










