闭包是函数与其定义时词法作用域的组合,能访问并记住外部变量;需满足三条件:存在嵌套函数、内部函数被外部引用、实际访问外部局部变量;其本质是[[environment]]静态绑定词法环境,变量因被闭包持有而从栈迁移至堆驻留。

闭包是指一个函数与其词法作用域的组合,即该函数能够访问并记住它被定义时所在作用域中的变量,即使外部函数已经执行完毕、其执行上下文已被销毁。
闭包的核心构成条件
形成闭包需同时满足三个条件:
- 存在一个内部函数(嵌套函数)
- 内部函数在外部函数返回后仍被引用(如作为返回值、赋给全局变量、传入回调等)
- 内部函数实际访问了外部函数的局部变量(而非仅参数或全局变量)
闭包的底层形成机制
当 JavaScript 引擎执行一个函数时,会为其创建执行上下文,其中包含变量对象(VO)、作用域链(Scope Chain)和 this 绑定。关键点在于:
- 函数对象在创建时,内部属性 [[Environment]] 会被绑定到其定义时的词法环境(Lexical Environment),这个绑定是静态的、不可变的
- 当外部函数执行结束,其执行上下文本应被销毁,但如果其内部函数被保留且引用了外部变量,V8 等引擎会将这些被引用的变量从栈迁移至堆,并通过内部函数的 [[Environment]] 持续指向该词法环境
- 此时该词法环境不再属于任何活跃执行上下文,但因被闭包持有而无法被垃圾回收——这就是“变量被捕获”的本质
一个典型闭包示例的执行过程
以常见计数器为例:
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
分析过程:
-
createCounter 执行时:在栈上创建执行上下文,声明
count;返回内部函数时,该函数的 [[Environment]] 指向当前词法环境(含count) -
createCounter 返回后:其执行上下文出栈,但
count因被内部函数闭包引用,被保留在堆中 -
每次调用 counter():新执行上下文的作用域链首项是自身词法环境,第二项即通过 [[Environment]] 链接到的外层词法环境(含
count),因此可读写该变量
闭包与内存管理的关键事实
闭包不是“魔法”,而是引擎对词法环境生命周期的精确控制:
- 未被任何闭包引用的变量,在函数退出后正常释放(不会造成泄漏)
- 只要闭包函数对象还存活,它所捕获的变量就持续存活——哪怕只用了一次,也需整个词法环境驻留
- 过度捕获(如在循环中无意闭包大量变量)或长期持有闭包引用(如事件监听器未解绑),才真正导致内存问题
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











