从 LHS(Left-Hand Side) 和 RHS(Right-Hand Side) 的角度,结合 JavaScript 引擎、编译器和作用域系统 来完整解析 var a = 1; 这一行代码背后发生了什么。
🧠 核心流程回顾
在 JavaScript 中,任何变量的使用都涉及两个基本操作:
| 类型 | 含义 | 示例 |
|---|---|---|
| LHS 查询 | 找到变量的位置,以便为其赋值 | a = 1 |
| RHS 查询 | 找到变量的值,用于读取或使用 | console.log(a) |
✅ 分析 var a = 1;
我们将这行代码拆分为几个阶段,并分别分析 LHS 和 RHS 的行为。
🔍 第一步:编译阶段(Compiler)
var a = 1;
编译器做了什么?
-
识别变量声明
var a- 在当前作用域中注册一个变量名
a。 - 这个过程叫“变量提升”(Hoisting),只声明不赋值。
- 目的是告诉引擎:“这个变量我提前声明了,你执行的时候不要报错。”
- 在当前作用域中注册一个变量名
-
绑定赋值操作
- 编译器知道等一下要执行
a = 1,也就是一次 LHS 查询(给变量a赋值)。
- 编译器知道等一下要执行
编译器总结:
- 注册变量
a到当前作用域。 - 准备好后续执行时对
a的 LHS 操作。
⚙️ 第二步:执行阶段(Engine)
当 JS 引擎开始执行这一行代码时:
var a = 1;
引擎会做两件事:
1. LHS 查询:找到变量 a 的位置
- 引擎问作用域:“我在当前作用域能给
a赋值吗?” - 如果可以,则允许赋值。
2. 将值 1 赋给 a
- 实际上是把内存中的值写入变量
a的存储空间。
⚠️ 注意:这里没有 RHS 查询,因为右边的
1是字面量,不是变量。
📌 总结:var a = 1; 全过程详解
| 阶段 | 行为 | 是否有 LHS/RHS |
|---|---|---|
| 编译阶段 | 编译器注册变量 a 到当前作用域 | ❌ |
| 编译阶段 | 编译器准备赋值动作 a = 1 | ✅ LHS(预处理) |
| 执行阶段 | 引擎查找 a 的位置(LHS 查询) | ✅ LHS |
| 执行阶段 | 引擎将值 1 写入 a | ❌ |
🧩 举个更复杂的例子:带 RHS 查询
var b = a;
这段代码中包含:
编译阶段:
- 声明变量
b - 准备执行
b = a
执行阶段:
- LHS 查询
b:找到变量b的位置以赋值。 - RHS 查询
a:获取变量a的值,然后赋给b。
🎯 LHS vs RHS 精准定义
| 类型 | 触发条件 | 行为 | 报错情况 |
|---|---|---|---|
| LHS | 给变量赋值 | 查找变量的位置(用于写入) | 找不到且非严格模式 → 自动创建全局变量;严格模式下报错 |
| RHS | 使用变量的值 | 查找变量的值(用于读取) | 找不到 → ReferenceError |
🧱 结合作用域机制
-
var a = 1;中的a会被注册在:- 当前函数作用域(如果在函数内)
- 或者全局作用域(如果不在任何函数中)
-
如果你在另一个作用域中访问
a,引擎会沿着作用域链向上查找。
📝 最终总结图解
源码:var a = 1;
[编译阶段]
└── 编译器识别 var a 并提升
└── 记录赋值操作 a = 1 (准备 LHS)
[执行阶段]
└── Engine: LHS 查询 a → 找到作用域中的 a
└── 将 1 赋值给 a
var a=b
编译器识别var a 并提升 -> 通过RSH 查询 a ->找到作用域中的a -> 通过LHS 查询 将 a的值赋值给 b