作用域和闭包是一体两面:作用域决定变量可见性,闭包使函数能携带并持久化定义时的词法作用域链;JavaScript采用词法作用域,函数查找变量始终依据定义时嵌套结构,闭包通过持有外层变量引用阻止其被回收,实现私有状态与数据封装。

作用域和闭包不是两个独立概念,而是一体两面:作用域定义“变量在哪能被看到”,闭包则是函数把那个“能看到的环境”打包带走的能力。它们协同工作的本质,是函数在创建时固化了词法作用域链,并在后续调用中持续沿该链查找变量。
词法作用域是闭包成立的前提
JavaScript 采用词法作用域(静态作用域),即函数的作用域在代码写完、函数被定义时就已确定,不随调用位置改变。这为闭包提供了稳定基础。
- 函数内部访问变量,总是按定义时的嵌套结构向上查找,而不是按执行时的位置
- 哪怕函数被返回到别处执行,它依然“记得”自己诞生时那一层一层包裹它的外层作用域
- 例如:outer 函数内定义的 inner,无论 later 在哪调用,都仍能访问 outer 的局部变量
闭包让作用域链“活下来”
通常,函数执行完毕后,其局部变量会随执行上下文销毁,被垃圾回收。但一旦形成闭包,引擎会保留该函数所依赖的外层变量引用,使对应作用域链节点无法释放。
- 闭包函数持有对外层变量对象(VO)的引用,不是拷贝值,而是活链接
- 只要闭包函数还存在(比如被赋值给全局变量、传入事件监听器、存入数组),外层变量就不会被 GC 回收
- 这是数据私有化和状态保持的底层机制——变量生命周期由闭包延长,而非函数执行周期
作用域链是变量查找的实际路径
每次访问变量,JS 引擎都沿着作用域链逐级搜索:从当前函数的活动对象(AO)开始,依次向上匹配外层作用域的变量对象,直到全局对象(window 或 globalThis)。
立即学习“Java免费学习笔记(深入)”;
- 闭包不改变查找逻辑,只是确保这条链在函数执行结束后依然有效可访问
- 查找过程完全透明:你写 console.log(x),引擎自动走链,无需手动操作
- 若某层作用域中声明了同名变量,就近原则生效——内层遮蔽外层,这也是作用域嵌套的天然行为
典型协同场景:计数器与模块封装
一个简单例子就能体现二者如何配合:
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
- count 属于 createCounter 的函数作用域,本应在函数返回后消失
- 但内部匿名函数引用了它,且被返回出去 → 形成闭包
- 每次调用返回的函数时,它都沿当初定义时的作用域链找到那个“活着”的 count 变量
- 结果:变量私有、状态持久、外部无法直接修改 —— 作用域限制 + 闭包维持 = 安全封装


















