词法环境(LexicalEnvironment)负责运行时变量查找与作用域链构建,管理let/const/参数/this等;变量环境(VariableEnvironment)专管var和函数声明的初始化与提升,二者在创建阶段同源,执行阶段分工明确。

JavaScript 函数执行上下文里,LexicalEnvironment(词法环境)和 VariableEnvironment(变量环境)是两个独立但紧密协作的内部组件,它们都属于执行上下文(Execution Context),但职责不同、行为不同、影响的作用域机制也不同。
词法环境(LexicalEnvironment)管“怎么找变量”
它决定运行时变量和 this 的查找路径。
当你写 console.log(x) 或访问 this,引擎就从当前 LexicalEnvironment 开始,沿着它的 outer 引用逐层向上查找,直到全局环境——这就是作用域链的实质。
- 存储内容:
-
let/const声明的绑定(含块级作用域,比如if、for内部) - 函数参数
-
catch子句的异常绑定 - 箭头函数的
this(词法继承)
-
- 特点:
- 支持块级作用域,进入/退出块时会创建或销毁对应的词法环境
-
let/const变量在声明前处于 TDZ(暂时性死区),访问报错 -
outer指向外部词法环境,构成静态可追溯的作用域链
例如:
function foo() {
let x = 1;
{
let x = 2;
console.log(x); // 2 → 当前块的 LexicalEnvironment 中的 x
}
console.log(x); // 1 → 外层函数 LexicalEnvironment 中的 x
}变量环境(VariableEnvironment)管“哪些东西能被提前声明”
它只负责var 声明和函数声明的初始化与绑定,是执行上下文创建阶段就“拍板定案”的那一份快照。
- 存储内容:
-
var声明(初始化为undefined) - 函数声明(直接绑定为函数对象)
-
- 特点:
- 在执行上下文创建阶段(Creation Phase)就完成全部绑定
- 不随块级结构变化——
var在if或{}里声明,依然属于函数级变量环境 - 是造成“变量提升”现象的底层机制
例如:
function bar() {
console.log(a); // undefined(不是 ReferenceError)
if (false) {
var a = 10; // 仍会被提升到函数级 VariableEnvironment
}
}注意:虽然规范中
VariableEnvironment本身也是一个词法环境类型(LexicalEnvironment的子类),但它不用于运行时查找,只用于声明阶段的绑定管理。真正参与变量读取的是LexicalEnvironment。
它们的关系不是“谁包含谁”,而是“谁服务谁”
-
创建阶段:
-
VariableEnvironment和LexicalEnvironment初始指向同一个环境记录(比如函数的DeclarativeEnvironmentRecord) - 但一旦遇到块级结构(如
{ let b = 1 }),LexicalEnvironment会切换为新的块级环境,而VariableEnvironment保持不变
-
-
执行阶段:
- 所有变量读取(
x)、this、super都走LexicalEnvironment链 -
var赋值、函数调用等操作,最终也依赖LexicalEnvironment查找,但它的初始绑定来自VariableEnvironment的创建结果
- 所有变量读取(
简言之:
-
VariableEnvironment是“声明登记处”(尤其对var和函数) -
LexicalEnvironment是“运行时寻址簿”(对所有标识符,包括let/const/this/参数等)
实际调试中怎么观察?
现代 DevTools(如 Chrome)的“Scope”面板显示的 “Closure”、“Function”、“Block”、“Global” 等层级,本质上就是 LexicalEnvironment 链的可视化呈现;而 var 变量总出现在最外层函数作用域下,正反映了 VariableEnvironment 的静态绑定特性。
你不需要手动操作这两个环境,但理解它们的分工,就能一眼看穿:
- 为什么
let报 TDZ 错误而var不报 - 为什么
var在块内声明却能在块外访问 - 为什么闭包捕获的是
LexicalEnvironment链,而不是某个“变量快照”
不复杂,但容易忽略。


















