闭包是词法环境的持久化结果,而非执行上下文切换策略;函数创建时通过[[Environment]]静态绑定外层词法环境,该环境驻留堆中并受引用保护,不随ECStack弹出而销毁。

闭包不是执行上下文切换的“策略”,而是执行上下文自然留存的结果。它不靠主动切换,而是依赖词法作用域和执行上下文的生命周期管理机制。
闭包的本质是词法环境的持久化
函数在定义时就捕获了其所在位置的词法环境(即外层函数或全局的变量对象、作用域链),这个绑定发生在函数创建时刻,而非调用时刻。即使外层函数执行完毕、其执行上下文从调用栈中弹出,只要内层函数仍被引用,对应的词法环境就不会被垃圾回收。
- 闭包中的自由变量(如 outerVariable)实际存储在外部函数的词法环境(LexicalEnvironment)中
- 内层函数的 [[Environment]] 内部属性始终指向该词法环境
- 这与“切换”无关——没有上下文被切换进来或切出去,只是原有环境被保留并持续可访问
执行上下文栈只负责运行时调度,不管理闭包生命周期
JavaScript 引擎用执行上下文栈(ECStack)管理函数调用顺序:调用时入栈,返回时出栈。但闭包所依赖的外层词法环境,并不随其执行上下文出栈而销毁。
- 例如:outer() 执行完,outer 的执行上下文出栈 → 但若 inner 被返回并赋值给变量,outer 的词法环境仍保留在内存中
- 引擎通过引用计数或标记-清除判断该环境是否可达;只要闭包函数还存在引用,环境就不可回收
- 所谓“切换策略”并不存在——上下文栈按调用顺序进出,闭包则静默持有对旧环境的引用
真正影响闭包行为的关键点
理解闭包与执行上下文的关系,重点不在“如何切换”,而在“何时保留、何时释放”:
- 变量声明方式有影响:用 var 声明的变量会挂载到变量对象(VO),而 let/const 声明的变量存在于词法环境的环境记录(Environment Record)中,两者在闭包中均可访问,但生命周期管理细节不同
- 函数是否被返回或传递出去:只有当内部函数脱离外层作用域使用(如作为返回值、事件回调、定时器参数),才构成真正有意义的闭包
- 避免意外闭包:循环中用 var + 函数,常因共享同一个变量绑定导致所有闭包引用同一值;改用 let 或立即执行可隔离每次迭代的绑定

















