一、从 var a = 1
看 JS 的两阶段核心机制
在 JavaScript 中,一句简单的 var a = 1
背后隐藏着编译阶段 和执行阶段的精密协作。
1. 编译阶段:声明变量
-
关键词
var
的使命 :javascriptvar a; // 编译器在编译阶段处理此声明
- 作用域登记 :编译器会在当前作用域(如全局或函数作用域)中"注册"变量
a
,但此时值为undefined
。 - 词法作用域 :变量的归属由声明时的位置决定,而非运行时(这是块级作用域与
var
的核心差异)。
- 作用域登记 :编译器会在当前作用域(如全局或函数作用域)中"注册"变量
2. 执行阶段:赋值操作
-
引擎的职责 :
javascripta = 1; // 引擎在执行阶段处理赋值
- LHS 查询 :引擎需要找到变量容器
a
(即=
左边的目标),若未找到:- 非严格模式 :自动在全局作用域隐式创建
a
(可能引发全局污染)。 - 严格模式 :直接抛出
ReferenceError
。
- 非严格模式 :自动在全局作用域隐式创建
- LHS 查询 :引擎需要找到变量容器
二、LHS 与 RHS:变量查找的"左右互搏"
JavaScript 对变量的操作分为两种模式,决定了作用域链的查询逻辑。
1. LHS(Left-Hand Side)查询
-
目标:找到变量容器本身,以便赋值。
-
典型场景 :
javascripta = 2; // 对 a 的 LHS 查询 function foo(b) { // 对参数 b 的隐式 LHS 查询(相当于 b = 实参) console.log(b); }
2. RHS(Right-Hand Side)查询
-
目标:获取变量的值。
-
典型场景 :
javascriptconsole.log(a); // 对 a 的 RHS 查询 const c = a + 1; // 对 a 的 RHS 查询
3. 查询失败的结果对比
查询类型 | 非严格模式 | 严格模式 |
---|---|---|
LHS | 自动创建全局变量 | 抛出 ReferenceError |
RHS | 抛出 ReferenceError |
抛出 ReferenceError |
三、作用域链:变量的"寻亲之路"
JavaScript 通过作用域链实现跨层变量访问,其规则可概括为:"由内向外,逐级查找,找到即停"。
1. 作用域嵌套示例
javascript
function outer() {
const b = 2;
function inner() {
console.log(b); // RHS 查询:inner作用域 → outer作用域 → 找到b=2
c = 3; // LHS 查询:inner作用域 → outer作用域 → 全局作用域 → 创建全局变量c
}
inner();
}
outer();
2. 作用域链示意图
sql
inner作用域 → outer作用域 → 全局作用域 → null
四、引擎、编译器、作用域的"三权分立"
JavaScript 的执行由三大角色共同完成,类似企业高管团队:
角色 | 职责类比 | 核心任务 |
---|---|---|
引擎 | CEO(决策者) | 控制代码执行流程,处理 LHS/RHS 查询 |
编译器 | CTO(技术实现者) | 解析代码,生成可执行指令(如变量声明) |
作用域 | COO(资源管理者) | 维护变量查找规则,管理作用域链 |
协作流程示例(var a = 1
)
- 编译器 :解析代码,在作用域中声明
a
(值为undefined
)。 - 引擎 :执行时对
a
进行 LHS 查询,找到后赋值为1
。 - 作用域 :提供变量查找路径,确保引擎能按规则访问
a
。
五、常见误区与最佳实践
1. var
的陷阱
-
变量提升 :
var
声明的变量会提升到作用域顶部,但赋值不提升。javascriptconsole.log(a); // 输出 undefined(而非报错) var a = 1;
-
无块级作用域 :在
for
循环或if
块中使用var
会导致变量泄漏。
2. 严格模式的重要性
- 启用方式 :在文件或函数顶部添加
"use strict";
。 - 优势:避免隐式全局变量,提前暴露潜在错误。
3. 优先使用 let/const
- 块级作用域 :解决
var
的作用域泄漏问题。 - 暂时性死区(TDZ):防止变量在声明前被访问。
总结
- 核心机制:JS 通过编译阶段的声明和执行阶段的赋值分离,配合作用域链实现变量管理。
- LHS/RHS:理解两种查询的区别是调试作用域问题的关键。
- 现代实践 :使用
let/const
替代var
,结合严格模式提升代码健壮性。
思考题:以下代码的输出是什么?为什么?
javascript
function test() {
console.log(b);
var b = 1;
}
test(); // ???