闭包是函数与其定义时词法环境绑定的运行时机制,需满足嵌套定义、引用外部自由变量、函数外传三个条件;它通过延长环境帧生命周期维持变量引用,而非复制值。

闭包不是一种语法糖,而是一种运行时机制——它把函数和它诞生时的词法环境“打包”在一起。所谓词法闭包,指的就是这种绑定发生在代码书写阶段(即静态分析时),变量引用关系由嵌套结构决定,与执行路径无关;而所谓“动态闭包”,其实并不存在于主流语言规范中——多数资料里提到的“动态闭包”多是误解或对延迟求值、运行时作用域链查找等现象的误称。真正被广泛支持、有明确定义的,只有词法闭包。
词法闭包的核心特征
词法闭包的形成依赖三个刚性条件:
- 嵌套定义:内部函数必须在外部函数体内声明(而非通过字符串 eval 或动态构造)
- 自由变量引用:内部函数实际访问了外部函数的局部变量(非参数、非自身声明、非全局)
- 函数作为返回值或外传引用:该内部函数需脱离原调用栈存活(如被 return、赋值给变量、传入回调等)
词法闭包如何在内存中持续存在
当外部函数执行结束,其执行上下文本应销毁,但若其中定义的内部函数已形成闭包,引擎会保留该上下文中的自由变量——不是复制值,而是维持引用。这靠的是“环境帧(Environment Record)”的延长生命周期:
- 每个函数调用生成独立的环境帧,记录当前作用域所有绑定
- 闭包函数对象内部持有一个 [[Environment]] 内部槽,指向其定义时的外层环境帧
- 只要闭包函数还被引用,它所绑定的环境帧就不会被垃圾回收
为什么没有真正的“动态闭包”
有些开发者把“在运行时决定捕获哪些变量”的行为称为动态闭包,比如:
一款AI工具,主要用于在主代理响应前,并行运行Kimi K2.5和GPT 5.3 Codex,注入双方观点以增强认知多样性,适合需要提升相关任务效率的用户。
function makeFn(x) { return function(y) { return x + y; }; } —— 这仍是词法闭包,x 的绑定在函数定义时就已确定,与调用时传什么值无关。
eval("function() { return x; }") —— 这不构成闭包,因为 eval 内部函数的作用域链不包含外层函数环境,只链接到全局或 eval 调用点环境。
真正能改变变量绑定时机的机制(如 Lisp 的动态作用域)在 JavaScript、Python、Java、Go 等主流语言中均未启用。它们全部采用词法作用域,因此闭包必然是词法的。
词法闭包的实际影响
理解这一点,能避开常见陷阱:
- 循环中创建多个闭包时,若都引用同一个变量(如
for (let i...)与for (var i...)行为不同),本质是环境帧是否为每次迭代新建 - 闭包携带的是变量引用,不是快照值——修改被捕获变量会影响所有共享它的闭包
- 过度保留大对象或 DOM 引用,可能引发内存泄漏,因为环境帧无法释放










