词法作用域在编译阶段即确定,由代码嵌套结构静态决定;引擎通过词法分析建立作用域链,函数定义时固化[[environment]]引用,变量查找与遮蔽均遵循该链,与运行时无关。

词法作用域在 JavaScript 编译阶段就已完全确定,不依赖运行时行为,只看代码写在哪、怎么嵌套。
词法分析阶段就锁定作用域结构
JavaScript 引擎(如 V8)拿到源码后,第一步是词法分析:把代码切分成一个个 token,比如 function、let、变量名、花括号等。这个过程不执行任何逻辑,纯粹“读结构”。
- 遇到 function outer() { ... },引擎立刻标记:这里开启一个新作用域,父级就是当前所在作用域(通常是全局)
- 若内部有 function inner() { ... },引擎直接确认 inner 的词法父级是 outer 的作用域
- let x、const y、var z 这些声明,会按其出现的位置,登记到对应层级的作用域记录中
函数定义时就固化 [[Environment]] 引用
每个函数在语法树(AST)生成后,引擎会为其绑定一个隐式内部属性 [[Environment]],它指向函数诞生时所处的词法环境。
- 比如 function greet(name) { let greeting = 'Hi'; return () => console.log(greeting, name); },箭头函数一被定义,它的 [[Environment]] 就已绑定到 greet 的作用域
- 即使 greet 执行结束、局部变量本该“消失”,这个链接仍存在——这就是闭包能捕获外部变量的根本原因
- 变量查找永远沿着这条编译期建好的链向上跳:当前作用域 → 外层作用域 → 全局作用域
遮蔽关系由书写位置静态决定
同名变量是否被遮蔽、哪个值会被读取,只取决于嵌套层级和声明位置,跟函数何时调用、从哪调用完全无关。
- let x = 1; function foo() { let x = 2; console.log(x); } 中,foo 内的 console.log 一定输出 2,因为编译时已确认内层 x 遮蔽了外层
- 哪怕 foo 是通过 setTimeout 延迟执行,或从另一个模块导入后调用,结果也不会变
- 这种静态可预测性,让 ESLint、VS Code 补全等工具能在编辑时就准确定位变量来源
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











