大家好,我是前端技术探索者FogLetter,今天要和大家聊聊JavaScript中一个既基础又容易踩坑的概念——作用域。很多同学在面试或者写代码时,经常遇到变量找不到、undefined、ReferenceError等问题,其实都是作用域在“作怪”。
这篇文章,我会用最通俗易懂的方式,结合代码示例和底层执行机制,带你彻底搞懂JS作用域!
📌 1. 从一句 var a = 1; 说起
先看这段代码:
var a = 1;
看起来很简单,但JS引擎在执行它时,经历了两个阶段:
- 编译阶段(CTO:编译器负责)
- 执行阶段(CCO:作用域负责)
1.1 编译阶段:var a;
JS引擎在运行代码前,会先进行词法分析,把代码拆解成可执行的单元:
var是声明关键字,告诉编译器要定义一个变量。a是变量标识符,会被存入当前作用域(内存中的某个位置)。- 此时,
a的值是undefined(未赋值)。
1.2 执行阶段:a = 1;
引擎开始执行代码:
- 查找变量
a(在作用域里找)。 - 找到了,就把
1赋值给它。
总结:
var a;→ 声明阶段(编译时)a = 1;→ 赋值阶段(运行时)
📌 2. 作用域:变量的“地盘”
变量不会凭空存在,它必须属于某个作用域。
作用域的本质:变量的查找规则。
2.1 作用域链:变量的“寻亲”过程
function foo() {
var a = 1;
function bar() {
console.log(a); // 1
}
bar();
}
foo();
bar里访问a,先在当前作用域(bar)找,没找到。- 去父级作用域(
foo)找,找到了a = 1。 - 如果还没找到,会一直向上查找,直到全局作用域(
window)。 - 如果全局也没有,就会报
ReferenceError(变量未定义)。
这就是作用域链!
当前作用域 → 父级作用域 → ... → 全局作用域 → 报错(没找到)。
📌 3. LHS vs RHS:变量查找的两种姿势
JS引擎查找变量有两种方式:
- LHS(Left-Hand Side):查找变量的容器(赋值操作)。
- RHS(Right-Hand Side):查找变量的值(引用操作)。
3.1 LHS(赋值操作)
a = 1; // LHS:查找变量 a,并赋值 1
- 如果找不到
a:- 非严格模式:JS会自动创建一个全局变量
a(危险!)。 - 严格模式(
'use strict'):直接报错ReferenceError。
- 非严格模式:JS会自动创建一个全局变量
3.2 RHS(取值操作)
console.log(a); // RHS:查找变量 a 的值
- 如果找不到
a:- 直接报错
ReferenceError: a is not defined。
- 直接报错
🌰 经典面试题
function test() {
a = 1; // LHS:非严格模式,自动创建全局变量 a
var b = 2; // b 是局部变量
}
test();
console.log(a); // 1(全局变量)
console.log(b); // ReferenceError: b is not defined
结论:
- LHS 找不到变量时,非严格模式会“偷偷”创建全局变量(容易导致 bug)。
- RHS 找不到变量时,直接报错。
📌 4. undefined vs ReferenceError
很多同学容易混淆这两个错误:
undefined:- 变量声明了但没赋值,默认是
undefined。
var a; console.log(a); // undefined- 变量声明了但没赋值,默认是
ReferenceError:- 变量根本没声明,直接报错。
console.log(b); // ReferenceError: b is not defined
📌 5. 词法作用域 vs 动态作用域
JS采用的是词法作用域(静态作用域):
- 作用域在代码编写时就确定了,而不是运行时。
- 和动态作用域(如 Bash)不同。
🌰 示例
var a = 1;
function foo() {
console.log(a);
}
function bar() {
var a = 2;
foo();
}
bar(); // 输出 1 还是 2?
答案:1
因为 foo 的作用域在定义时就确定了,和 bar 里的 a 无关。
📌 6. 作用域面试题
6.1 变量提升(Hoisting)
console.log(a); // undefined
var a = 1;
原因:
var声明的变量会提升到作用域顶部,但赋值不会。
6.2 块级作用域(let/const)
if (true) {
let b = 2;
var c = 3;
}
console.log(b); // ReferenceError: b is not defined
console.log(c); // 3
结论:
var没有块级作用域,let/const有。
📌 7. 总结
- JS执行分两阶段:编译(声明) + 执行(赋值)。
- 作用域是变量的查找规则,遵循作用域链。
- LHS(赋值) vs RHS(取值):
- LHS 找不到变量时,非严格模式会创建全局变量(危险!)。
- RHS 找不到变量时,直接报错。
undefinedvsReferenceError:undefined→ 声明但未赋值。ReferenceError→ 变量未声明。
- 词法作用域在代码编写时就确定了,和调用位置无关。
🎯 最后
理解作用域是JS进阶的核心基础,希望这篇文章能帮你彻底掌握它!
如果你觉得有用,点赞 + 收藏支持一下!🔥
关注我,解锁更多前端硬核知识!🚀
评论区互动:
👉 你在写代码时,遇到过哪些作用域相关的问题?
👉 你觉得 var 和 let 哪个更好?为什么?
我们下期见!💪