闭包是函数与其定义时词法作用域的绑定,本质为[[Environment]]静态绑定活引用;需满足三条件:嵌套函数、内层函数引用外层变量、内层函数在外层作用域外执行。

闭包与词法作用域的深度解构,核心逻辑在于:函数在定义时就“冻结”了它能看到的变量环境,并把这个环境绑定到函数本身——不是复制值,而是持有一个活的引用。
词法作用域决定“能看见谁”
词法作用域是静态的,由代码书写位置决定,而非运行时调用位置。一个函数内部能访问哪些变量,完全取决于它写在哪一层嵌套里。
- 函数内部可以直接读写外层函数声明的 let/const/var 变量,只要没被遮蔽(shadowing);
- 哪怕外层函数已执行完毕、执行上下文早已弹出调用栈,只要内层函数还存在,它依然能通过词法环境链找到那些变量;
- 这种“看得见”不依赖执行路径,只依赖源码结构——比如把函数传给
setTimeout或作为事件回调,它照样能访问定义时的外层变量。
闭包是词法作用域的“延续载体”
闭包不是特殊语法,而是一种运行时现象:当一个函数引用了其外层词法作用域中的变量,并且该函数在外部被调用,JS 引擎就必须保留那个外层作用域的内存空间,不能回收。
- 变量不会被销毁,是因为有活跃的引用链:闭包函数 → 词法环境 → 外层变量;
- 这个被保留的词法环境,就是所谓的“环境快照”,但它不是快照(snapshot),而是实时可读写的活引用;
- 多个闭包可以共享同一份外层变量(如循环中生成的多个函数),修改其中一个,其他闭包也能感知到变化。
作用域链是查找变量的实际路径
每次访问变量时,JS 引擎都沿着作用域链逐层向上查找,起点是当前函数的词法环境,终点是全局环境。
- 作用域链不是运行时动态构建的,而是在函数创建时就确定下来,固化在函数对象的
[[Environment]]内部槽中; - 它本质上是一条指向外层词法环境的单向链表,保证了“从内到外、静态可追溯”的查找逻辑;
- 闭包之所以有效,正是因为作用域链没有断裂——即使外层函数执行结束,链上的节点仍被闭包持有。
闭包的三个硬性条件缺一不可
只有同时满足以下三点,才会形成真正意义上的闭包:
- 存在嵌套函数结构(内层函数定义在外层函数内部);
- 内层函数实际引用了外层函数的变量或参数(不只是声明,而是被读取或赋值);
- 内层函数被带到外层作用域之外使用(例如 return 出去、赋值给全局变量、传给异步回调等)。
不复杂但容易忽略:闭包不是设计出来的技巧,而是词法作用域 + 引用计数垃圾回收机制共同作用下的自然结果。理解它,关键不是记结论,而是看清变量生命周期如何被函数引用“延长”。

















