闭包是函数与其创建时词法环境的组合,依赖执行上下文的保留机制:当内部函数被外部持有,其对内层上下文的持续引用会阻止垃圾回收,使变量通过作用域链持久可访问。

闭包不是凭空出现的,它依赖于 JavaScript 执行上下文的创建与生命周期规则。理解闭包,关键在于看清执行上下文如何被保留——不是函数“记得”变量,而是引擎因引用关系而不得不留下它的上下文。
执行上下文是闭包存在的前提
每当函数调用时,JS 引擎会为其创建一个执行上下文,其中包含:
- 变量对象(含参数、
let/const/var声明) - 作用域链(指向外层词法环境的只读链表)
-
this绑定和arguments(若适用)
正常情况下,函数执行结束,其执行上下文会被弹出调用栈并准备被垃圾回收。但一旦内部函数被外部变量持有,情况就不同了。
闭包让执行上下文“延迟销毁”
闭包的本质,是内部函数对其外层执行上下文的**持续引用**。这种引用会阻止外层上下文被回收,从而保住其中的变量。
- 外层函数返回内部函数时,该内部函数的
[[Environment]]内部槽仍指向外层的词法环境 - 只要内部函数还被某个变量(如
counter)引用,外层上下文中的变量对象就不会释放 - 多个闭包可共享同一外层上下文(比如多次调用
makeAdder(5),每个返回函数都各自捕获独立的x)
作用域链是闭包生效的路径
闭包访问变量,并不靠“记忆”,而是靠作用域链的动态查找机制:
- 每次执行闭包函数,引擎沿其作用域链逐级向上查找标识符
- 查找到的第一个匹配变量即为所用(遵循词法作用域,非调用时的作用域)
- 即使外层函数早已退出,作用域链中那段指向它的环节依然有效
闭包不是“函数本身”,而是“函数+环境”的组合
标准定义强调:闭包 = 函数 + 其创建时的词法环境。这意味着:
- 同一个函数表达式,在不同位置定义,会产生不同的闭包(环境不同)
-
function foo() { return function() { return x; } }中,返回的函数是否构成闭包,取决于x是否来自foo作用域 - 箭头函数也形成闭包,因为它隐式继承外层的
this和词法环境











