闭包能长期访问外部变量,靠的是堆内存中的词法环境记录及函数的[[environment]]引用;执行上下文栈仅临时调度,不保存变量或作用域,lexicalenvironment存在于堆中且生命周期独立于栈帧。

函数执行栈(ECStack)只管函数“进”和“出”,不决定变量或对象能不能留着;闭包能长期访问外部变量,靠的是堆内存里的词法环境记录,以及函数内部的 [[Environment]] 引用——不是栈上没清掉,而是堆里被稳稳拉着没被回收。
执行上下文栈只是临时调度员
每次函数调用,JS 引擎就新建一个执行上下文,压进栈里;函数一返回,这个上下文立刻弹出销毁。栈里不留任何变量,也不保留作用域信息。
- 栈中只存上下文的引用(比如 LexicalEnvironment 的指针),不是环境本身
- LexicalEnvironment 是个对象,分配在堆内存中,和栈帧生命周期无关
- outer 函数返回后,它的执行上下文早已消失,但堆里的那块环境记录还在——只要有人引用它
闭包靠 [[Environment]] 绑定堆中环境
函数创建时,引擎就把它定义时的词法环境写进内部属性 [[Environment]],这个绑定静态、不可更改。
- inner 函数的 [[Environment]] 指向 outer 执行时生成的堆环境记录
- 多个闭包(如 inc 和 get)可共享同一个堆环境记录,读写的是同一份数据
- let/const 声明的变量,在闭包中不是拷贝值,而是直接更新堆中那个绑定的值
闭包存活 = 堆环境记录不被回收
对象或变量能不能留着,取决于有没有“可达引用”。闭包就是最典型的维持引用的方式。
- 只要至少一个闭包函数还存在且可达,它所引用的堆环境记录就不会被垃圾回收
- 全局变量保存了闭包函数 → 环境记录一直驻留
- 事件监听器、定时器回调用了闭包 → 同样会延长相关变量的生命周期
- 没有闭包引用,环境记录变成垃圾,下次 GC 就清理掉
常见误解要避开
很多说法听起来合理,但和实际内存机制不符:
- ✘ “闭包让变量留在栈上” → 栈帧早就没了,变量根本不在栈里
- ✘ “闭包复制了变量值” → 它捕获的是对堆中绑定的引用,不是副本
- ✘ “函数返回,内部对象就该销毁” → 只要被闭包引用,就继续活着
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











