闭包是函数与其定义时词法环境的绑定,通过内部[[environment]]属性静态记录,不依赖运行时查找;作用域链按自身→外层→全局顺序查找变量,即使外层函数执行完毕,其变量仍因被引用而无法回收。

因为 JavaScript 采用词法作用域,函数在定义时就绑定了其外层作用域的引用,这个绑定通过内部的 [[Environment]] 属性实现,不是靠运行时查找,而是靠静态记录。
词法作用域决定“记住”的时机
函数的作用域范围在写代码时就固定了,和它在哪被调用无关。只要函数声明出现在某个作用域内,它就自动捕获该作用域的变量环境。
- 比如
function inner() { return x }写在function outer() { let x = 1 }里面,inner就天然“知道”自己能访问x - 这个关系在解析阶段就建立,不依赖
outer是否执行、是否结束
作用域链是函数自带的“寻址路径”
每个函数对象内部都维护一条作用域链,按顺序指向:自身词法环境 → 外层函数词法环境 → 全局词法环境。执行时按此链逐级查找变量。
-
[[Environment]]是隐式属性,开发者不可见但引擎全程使用 - 即使
outer()执行完退出,它的词法环境仍被inner的作用域链持有,不会被垃圾回收 - 多个闭包可各自持有同一外层作用域的不同快照(如循环中生成多个函数)
闭包不是特殊语法,而是自然结果
只要满足三个条件,闭包就自动产生:函数嵌套 + 内部函数引用外部变量 + 外部函数返回或传出内部函数。没有 close() 或 makeClosure() 这样的手动操作。
- 常见表现:计数器、事件处理器、柯里化函数、模块私有变量
- 关键不是“函数被返回”,而是“函数保留了对外部变量的引用”
- 哪怕没返回,只要内部函数还存活(如被赋值给全局变量),闭包就存在
为什么不会丢失变量?内存管理角度
JavaScript 引擎判断变量是否可回收,看是否有活跃引用。闭包让外层函数的局部变量被内部函数持续引用,因此这些变量无法被释放。
- 这不是“延长生命周期”,而是变量本来就在作用域链上,一直可达
- 滥用闭包(如无意保留 DOM 节点或大对象)会导致内存泄漏
- 现代引擎会做优化,如果闭包实际没用到某个外层变量,可能不保留它
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











