所属板块:2. 执行上下文与闭包(JS 的核心引擎)
记录日期:2026-03-xx
更新:遇到作用域链或变量查找相关输出题时补充
1. 作用域:变量的“合法活动范围”
作用域就是 JS 引擎规定“这个变量在哪一段代码里可以被访问”的边界规则。
如果把 [2-1] 里的执行上下文比作“容器”,那么作用域就是容器内部给每个变量划定的“活动区域”。
JS 采用的是词法作用域(Lexical Scope / 静态作用域),核心特点:
一个变量的作用域在代码书写(定义)的那一刻就已经确定,跟函数在哪里被调用完全无关。这和 this 的“调用时动态绑定”形成鲜明对比,是 JS 引擎最稳定的查找法则。
(注意:JS 没有动态作用域,只有极少数老式语言如 Bash、Perl 才用动态作用域。)
2. JS 的三种作用域类型
-
全局作用域
- 最外层,变量默认挂在 window / global 上
- 容易造成污染,生产代码中应尽量避免
-
函数作用域(ES5 及之前的主力)
- 每个 function {} 形成独立作用域
- var 声明的变量只有函数作用域(没有块的概念)
-
块级作用域(ES6 新增)
- {} 配合 let / const 形成
- if、for、while、try-catch、单独的 {} 都能创建块级作用域
- 彻底解决了 var 的变量提升泄漏和全局污染问题
示例对比(经典 for 循环题):
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // 输出 3 3 3(var 是函数作用域)
}
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 0); // 输出 0 1 2(let 是块级作用域)
}
3. 作用域链(Scope Chain):变量查找的“导航链表”
当引擎需要读取或写入一个变量时,会按以下路径查找:
- 先在当前执行上下文的变量对象里找
- 找不到 → 顺着作用域链(每个上下文持有的 [[outer]] 指针)去外层上下文查找
- 一直找到全局作用域
- 全局还找不到 → 抛 ReferenceError
作用域链在 [2-1] 创建阶段就已经连好,不会因为函数调用位置而改变(词法作用域的体现)。
示例(多层嵌套 + 变量遮蔽):
var x = "global";
function outer() {
var x = "outer";
function inner() {
var x = "inner";
console.log(x); // "inner"(就近原则,先找自己的作用域)
}
inner();
}
outer();
4. LHS 查询 vs RHS 查询(引擎底层查找方式)
这是《你不知道的 JavaScript》里非常核心的概念,面试常考:
-
RHS 查询(Right-Hand Side):取值操作(读取变量)
示例:console.log(a)→ 查找 a 的值 -
LHS 查询(Left-Hand Side):赋值操作(写入变量)
示例:a = 2→ 查找 a 的位置准备赋值
如果 LHS 查询最终在全局都找不到,且不是严格模式,会自动创建全局变量(这是 var 的隐蔽 bug 根源之一)。
5. 小结 & 复习时的“引擎视角”
- 记住核心法则:作用域在定义时就定死了,顺着作用域链从内向外找
- 遇到“变量找不到”或“拿到的值不对”时,画一下作用域链(从当前函数 → 外层函数 → 全局)
- 优先使用 let / const 开启块级作用域,能大幅减少隐蔽 bug
- [2-1] 的执行上下文 + 本文的 作用域链,共同构成了后面闭包的底层基础
下一篇文章会进入 [2-3] 动态上下文 this 指向机制(五大绑定规则、手写 call/apply/bind 等)。
返回总目录:戳这里