闭包中自由变量的值来自函数定义时的词法作用域,而非执行位置;引擎按嵌套层级从定义处向上查找最近同名变量绑定,该过程静态、与调用栈无关。

词法作用域直接决定了闭包中自由变量的值从哪来、找谁要——不是看函数在哪执行,而是看它在哪儿写的。
自由变量只按定义位置向上查找
JavaScript 在函数**定义时**就固定了它的词法环境。当内部函数引用一个既非参数也非局部声明的变量时,引擎会沿着代码的嵌套结构,从当前函数体开始,逐层向外检查外层函数或全局作用域中**同名变量的最近定义处**。
- 这个查找过程完全静态,与调用栈、执行位置无关
- 哪怕外部函数早已执行完毕、执行上下文被销毁,闭包仍能通过保存的词法环境访问当初定义时的那个变量绑定
- 如果多层作用域都有同名变量,取的是**定义时路径上最先遇到的那个**,不是运行时调用链上的
var 声明循环中的 i 为什么总输出最后一个值?
这不是闭包“失效”,恰恰是词法作用域严格执行的结果:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
-
var i没有块级作用域,整个 for 循环共用一个i变量绑定 - 所有回调函数(如
setTimeout里的)都引用这同一个i,而它们的定义位置都在循环体外(或同一层) - 等异步任务执行时,循环早已结束,
i已变成终值(比如 5),所有闭包读到的都是它
对比 let 声明就能看清机制
用 let i 时,每次迭代都会创建一个新的绑定,每个回调函数在定义时捕获的是各自迭代轮次的独立 i —— 因为词法作用域为每个 let 绑定生成了不同的“位置”。所以:
-
for (let i = 0; i console.log(i), 0)输出 0、1、2 - 本质不是“let 修复了闭包”,而是
let提供了更细粒度的词法绑定,让每个闭包记住的是不同位置的自由变量
全局变量和嵌套函数的查找顺序
自由变量查找严格遵循嵌套层级,不跳过中间层:
- 内层函数先查自己作用域 → 没有就查外层函数的参数/变量 → 还没有就查再外层 → 最后到全局
- 即使全局有同名变量,只要外层函数里定义了,就优先取外层的;不会因为外层函数已返回就转而去找全局的
- 动态作用域(如某些 Lisp)才按调用链找,JS 完全不这么干
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










