词法作用域在编译阶段即固定,由函数和变量在源码中的书写位置决定;引擎通过词法分析构建作用域链,函数定义时就确定[[environment]]引用,变量查找依此链静态进行,遮蔽关系与执行顺序无关。

词法作用域在代码编译阶段就已固定,它不依赖运行时的调用顺序,而是由函数和变量在源码中书写的位置直接决定。
词法分析阶段就锁定作用域结构
JavaScript引擎(如V8)拿到源码后,第一步是词法分析:把代码拆成一个个词法单元(tokens),比如function、var、变量名、括号等。这个过程不执行代码,只“看”结构。
- 遇到function outer() { ... },引擎立刻标记:这里开启一个新作用域,其父级是当前所在作用域(可能是全局)
- 遇到function inner() { ... }写在outer内部,引擎就确定inner的词法父级是outer的作用域
- 所有var/let/const声明的位置,直接登记到对应作用域的变量记录中
作用域链在编译时生成雏形
每个函数在定义时,引擎会为其内部创建一个隐式的[[Environment]]引用,指向它诞生时所处的词法环境。这个链接关系不是运行时推出来的,而是编译完AST(抽象语法树)后就确定了。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 比如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、IDE自动补全)能在编辑时就准确提示变量来源
动态机制会破坏词法静态性
JavaScript特意限制了能干扰词法作用域的语法,因为它们会让编译阶段无法可靠建模:
- eval()执行字符串代码时,可能在运行时插入新变量,导致作用域不可预测
- with语句会临时把对象属性注入作用域链顶层,改变变量查找路径
- 现代严格模式下,这两个特性都被禁用或大幅削弱,就是为了守住词法作用域的可静态分析性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










