闭包是函数对象与其定义时作用域的绑定体,需同时满足嵌套定义、引用外部局部变量、外部函数返回内部函数三要素。它封装行为与状态,实现变量持久化和环境携带。

闭包不是“函数加变量”的简单拼凑,而是函数对象与其定义时所处作用域的绑定关系。它让一个函数能持续访问原本已该消失的外部局部变量,关键不在“有没有用到”,而在“是否形成可携带环境的函数实体”。
闭包的本质是函数 + 作用域快照
当内部函数被返回(或传递出去)时,Python 或 JavaScript 并不会只复制代码,而是把该函数和它当时能访问的所有外部变量“打包”成一个整体。这个整体就是闭包 —— 函数体是行为,外部变量是状态,两者不可分割。
- 外部函数执行完后,其栈帧本应销毁,但只要内部函数还被引用,这些变量就继续存在
- 每个闭包实例都独享一份外部变量副本(比如多次调用 outer(),每次得到的 inner 都有自己独立的 num)
- 这个“快照”是静态捕获的:它记住的是变量在内函数定义时所处的作用域链,而不是调用时的值
三要素缺一不可:嵌套、引用、返回
只有同时满足以下三点,才构成真正意义上的闭包:
- 结构上嵌套:inner 必须定义在 outer 内部
- 逻辑上引用:inner 显式使用了 outer 的局部变量(不能只是读 global 或参数)
- 行为上返回:outer 返回的是 inner 这个函数对象本身,而非 inner() 的执行结果
少一个条件,就只是普通嵌套函数或高阶函数,不是闭包。
闭包不是“偷变量”,而是“带环境出门”
很多人误以为闭包是绕过作用域规则的技巧,其实它恰恰严格遵守作用域链机制 —— 只是延长了变量生命周期。它的价值在于把一段计算逻辑和它依赖的数据“封装成一个可移动单元”:
- 工厂函数:make_adder(10) 返回一个“加10专用函数”,它自带 10 这个配置
- 私有状态:counter() 返回的 add 函数,把 total 变量锁在自己内部,外界无法直接修改
- 延迟求值:函数返回后不立刻执行,等需要时再调用,仍能拿到当初的上下文
注意:自由变量 ≠ 全局变量
闭包中能访问的外部变量叫“自由变量”(free variable),特指既非本地定义、也非全局声明,而是在外层函数中定义的局部变量。它不依赖 global 声明,也不靠参数传入,而是靠作用域链自然找到 —— 这正是闭包区别于普通回调的关键。
理解这点,就能明白为什么 closureFunc() 调用时还能输出 "Hello Closure":message 不在 global,也不在 inner 里定义,但它就在 outer 留下的作用域快照里,一直跟着 inner 走。











