闭包的核心是词法作用域链固化与执行上下文生命周期延长:函数定义时静态捕获外层变量引用,使该作用域不被垃圾回收;变量访问通过已固化的作用域链查找,而非复制值或复活执行上下文。

函数执行上下文嵌套闭包时,变量能被持续访问,核心在于词法作用域链的固化 + 执行上下文生命周期的延长。不是“变量被复制”,也不是“外层函数没结束”,而是内层函数在创建时就静态捕获了外层作用域的引用,并让该作用域不被垃圾回收。
词法作用域在函数定义时就已锁定
JavaScript采用词法作用域(静态作用域),函数的作用域关系在它被声明(写代码)那一刻就确定了,与后续在哪里调用无关。比如:
function outer(x) {
let y = 10;
return function inner() {
return x + y; // x 和 y 都是 inner 的自由变量
};
}
const fn = outer(5); // outer 执行完毕,但 inner 仍能访问 x=5、y=10
这里 inner 函数体中出现的 x 和 y,解析器在编译阶段就顺着代码结构向上查找到 outer 的作用域,并把这两个绑定记录进 inner 的词法环境(LexicalEnvironment)。这个绑定关系不会因 outer 执行结束而失效。
闭包让外层执行上下文“延迟销毁”
通常,函数执行完,它的执行上下文(含变量对象、作用域链等)会被从执行栈弹出,随后被垃圾回收。但若该函数内部创建的函数被外部保留(如作为返回值、赋值给变量、传入事件监听器等),JS 引擎会检测到仍有活跃引用指向其词法环境——于是保留该上下文中的变量对象,哪怕外层函数早已退出。
- 每个闭包都持有一个对“外层词法环境”的强引用
- 只要闭包存在,它所依赖的外层变量就不会被 GC 回收
- 多次调用
outer()会产生多个独立的闭包,各自维护自己那份x和y(即“每次调用生成新作用域”)
变量访问走的是作用域链查找,不是作用域“复活”
当 inner() 执行时,它会按自己的作用域链逐级查找变量:
- 先查自身执行上下文的变量对象(无
x/y) - 再查词法环境中的外层作用域(即
outer创建的那个变量对象) - 最后才到全局作用域
这个查找过程依赖的是已固化的作用域链结构,而非重新激活 outer 的执行上下文。所以即使 outer 已退出,inner 依然能准确找到当初定义它时所处的那套变量。
注意:闭包捕获的是变量“引用”,不是值快照
如果外层变量后续被修改,闭包读取到的是最新值:
function makeCounter() {
let count = 0;
return {
inc: () => ++count,
get: () => count
};
}
const c1 = makeCounter();
console.log(c1.get()); // 0
c1.inc();
console.log(c1.get()); // 1 —— 访问的是同一个 count 引用
这说明闭包保存的是对变量内存地址的引用,而不是拷贝一份值。这也是为什么多个闭包共享同一外层变量时会相互影响(除非每次调用都新建独立作用域)。

















