JavaScript执行上下文通过变量环境和词法环境协同管理作用域:变量环境在创建阶段初始化var、函数声明、形参及arguments;词法环境支持let/const和块级作用域,通过outer链构成作用域链,查找变量时沿链向上追溯,且链在函数定义时即固定。

JavaScript 执行上下文通过变量环境(Variable Environment)和词法环境(Lexical Environment)协同管理变量作用域,不是靠“作用域对象”本身,而是靠执行时的结构化环境链与阶段化处理机制。
变量环境负责声明绑定,区分 var 与函数声明
变量环境在执行上下文创建阶段就初始化,专门存放:
-
var声明的变量(初始值为undefined) - 函数声明(
function foo() {},直接绑定函数体) - 函数形参(在函数上下文中,对应实参值)
-
arguments对象(非箭头函数中)
它不处理 let/const,也不支持块级作用域。例如:
function test(x) {
var a = 1;
function inner() {}
}
test(42);此时 test 的变量环境中已有 x: 42、a: undefined、inner: [function] —— 全部在代码执行前就登记完毕。
立即学习“Java免费学习笔记(深入)”;
词法环境处理块级作用域和 let/const
词法环境在变量环境基础上进一步构建,用于支撑 ES6+ 的块级作用域特性:
- 存储
let、const声明的绑定 - 每个
{}块(如if、for、{}直接块)都可能创建新的词法环境 - 包含一个
outer引用,指向父级词法环境,形成作用域链基础
比如:
function outer() {
let x = 'outer';
if (true) {
let y = 'inner';
console.log(x, y); // ✅ 可访问
}
console.log(x); // ✅ 可访问
// console.log(y); // ❌ ReferenceError
}if 块内新建了一个词法环境,y 绑定在此环境中;块结束,该环境被释放,y 不再可访问。
作用域链由词法环境链构成,查找变量靠“向上追溯”
作用域链不是独立数据结构,而是当前词法环境及其 outer 链组成的只读路径。变量访问过程是:
- 先查当前词法环境的绑定
- 找不到,就沿
outer查父级词法环境 - 一直查到全局词法环境(浏览器中是
window的词法环境) - 还找不到,抛出
ReferenceError
关键点在于:这个链在函数定义时就固定了,与调用位置无关。这就是词法作用域的本质。
创建与执行两阶段决定变量可见性
每个执行上下文分两个不可跳过的阶段:
-
创建阶段:初始化变量环境、词法环境、
this、作用域链 -
执行阶段:按顺序赋值
var、执行let/const初始化(触发 TDZ 检查)、运行语句
因此:
-
var a; console.log(a)→ 输出undefined(已声明未赋值) -
console.log(b); let b = 2→ 报错ReferenceError(处于暂时性死区)
这背后是环境对象在不同阶段的可用性差异,而非语法糖或编译魔法。
不复杂但容易忽略


















