闭包是函数执行后其词法环境被保留而动态形成的引用关系,核心在于外层函数执行结束时因内层函数引用而延迟回收变量,捕获的是变量绑定而非值快照。

闭包不是“创建时就自动产生”的静态结构,而是 JavaScript 执行流程中动态形成的引用关系——它发生在函数被定义、执行、返回、调用这一连串环节里,核心在于“词法环境被保留”这一结果。
词法环境在函数定义时就已锁定
JavaScript 是词法作用域语言。函数体内的变量查找规则,在代码写好那一刻就确定了,不依赖运行时调用位置。比如:
function outer() {
const x = 10;
return function inner() {
console.log(x); // 这里明确引用 outer 的 x
};
}
inner 函数在定义时,引擎就记录下它需要访问 outer 的词法环境(含 x),这个绑定是静态的、不可更改的。
闭包真正“形成”于外层函数执行结束之后
outer 调用完毕本该销毁其执行上下文(包括变量对象 VO),但因为 inner 被返回并赋值给外部变量(如 const fn = outer();),V8 引擎检测到 inner 的 [[Environment]] 仍指向 outer 的词法环境,于是保留该环境不回收。
此时才真正构成闭包:inner + 它所捕获的 outer 词法环境(含 x)作为一个整体存在。
变量生命周期由闭包引用决定,而非作用域退出
普通局部变量随函数执行上下文销毁而释放;但被闭包引用的变量,会一直保留在内存中,直到闭包函数本身不再被任何变量引用(即进入 GC 可回收状态)。
- 只要 fn 还存在,x 就不会被垃圾回收
- 若后续执行 fn = null;,且无其他引用,V8 才可能在下次 GC 时清理 x 和 outer 的词法环境
闭包捕捉的是“变量绑定”,不是值快照
闭包捕获的是对外部变量的**引用**,不是复制一份值。这意味着:
function createCounter() {
let count = 0;
return function() {
return ++count;
};
}
const inc = createCounter();
console.log(inc()); // 1
console.log(inc()); // 2
两次调用 inc 都访问同一个 count 绑定,它的值持续更新——这正是闭包维持状态能力的根源。











