let和const在块级作用域的行为由词法环境直接管理:每个{}块新建独立词法环境,含未初始化的声明式环境记录;tdz源于绑定“已创建未初始化”状态;重复声明在编译阶段检查;不进变量环境,无值提升,仅声明提升。

JavaScript 中 let 和 const 在块级作用域里的行为,核心是由词法环境(Lexical Environment) 直接管理的,而不是像 var 那样依赖变量环境(Variable Environment)。这种设计支撑了块级作用域、暂时性死区(TDZ)和重复声明报错等关键特性。
词法环境为每个块创建独立的环境记录
当执行进入一个 `{}` 块(比如 `if`、`for` 或裸 `{}`)时,如果其中有 `let` 或 `const` 声明,引擎会为该块**新建一个词法环境**,并把它链接到外层词法环境的 `outer` 引用上。这个新环境包含: - 一个**声明式环境记录(Declarative Environment Record)**,专门存放 `let`/`const` 绑定; - 该记录不支持动态属性赋值(不像对象环境),只通过声明语句初始化绑定; - 每个绑定初始处于“未初始化”状态,直到对应 `let x` 或 `const y = ...` 语句执行。例如:
```js function foo() { let a = 1; if (true) { let b = 2; console.log(b); // ✅ 可访问 } console.log(b); // ❌ ReferenceError:b 不在当前词法环境中 } ``` `if` 块的词法环境是函数词法环境的子环境,`b` 的绑定只存在于该子环境中,外部无法查找到。TDZ(暂时性死区)由词法环境的初始化状态控制
词法环境中的 `let`/`const` 绑定在声明语句执行前,处于“已创建但未初始化”状态。此时任何读写操作都会触发 TDZ 错误:-
console.log(x)在let x之前 →ReferenceError: Cannot access 'x' before initialization -
x = 10在let x之前 → 同样报错 -
typeof x在let x之前 → 也报错(不同于var的"undefined")
这是因为词法环境记录内部维护了每个绑定的初始化标志位;只有执行到声明语句时,标志才置为“已初始化”,变量才真正可用。
重复声明检查发生在词法环境构建阶段
同一个词法环境内,不允许重复声明同名的 `let` 或 `const` 变量:-
let x; let x;→ SyntaxError -
const y = 1; const y = 2;→ SyntaxError -
let x; const x = 1;→ SyntaxError(同名冲突,不管关键字)
这个检查不是运行时的,而是在**解析或编译阶段**,根据当前词法环境记录中已注册的绑定名称完成的。它不跨环境——不同块里同名 `let` 是允许的,因为它们属于不同的词法环境记录。
与 var 的关键区别:不进变量环境,也不提升初始化
- `var` 声明会同时写入**变量环境**和词法环境,且在执行前就被初始化为 `undefined`; - `let`/`const` **只写入词法环境**,且初始化动作严格绑定到声明语句执行时刻; - 所以它们不存在“值提升”,只有“声明提升”(即绑定被提前注册,但不可访问)。这也解释了为什么:
- 循环中 `for (let i...)` 每次迭代都新建词法环境 → `i` 是全新绑定,闭包能正确捕获不同值;
- `const` 要求必须初始化 → 因为词法环境记录要求 `const` 绑定在声明时就必须赋予确定值;
- 块级作用域能真正隔离变量 → 词法环境链天然支持嵌套与边界切断。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











