🔥 20万前端学习者都在问的JS作用域核心原理,这次彻底讲透!

174 阅读4分钟

大家好,我是前端技术探索者FogLetter,今天要和大家聊聊JavaScript中一个既基础又容易踩坑的概念——作用域。很多同学在面试或者写代码时,经常遇到变量找不到、undefinedReferenceError等问题,其实都是作用域在“作怪”。

这篇文章,我会用最通俗易懂的方式,结合代码示例底层执行机制,带你彻底搞懂JS作用域!


📌 1. 从一句 var a = 1; 说起

先看这段代码:

var a = 1;

看起来很简单,但JS引擎在执行它时,经历了两个阶段

  1. 编译阶段(CTO:编译器负责)
  2. 执行阶段(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引擎查找变量有两种方式:

  1. LHS(Left-Hand Side):查找变量的容器(赋值操作)。
  2. RHS(Right-Hand Side):查找变量的(引用操作)。

3.1 LHS(赋值操作)

a = 1; // LHS:查找变量 a,并赋值 1
  • 如果找不到 a
    • 非严格模式:JS会自动创建一个全局变量 a(危险!)。
    • 严格模式('use strict':直接报错 ReferenceError

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. 总结

  1. JS执行分两阶段:编译(声明) + 执行(赋值)。
  2. 作用域是变量的查找规则,遵循作用域链
  3. LHS(赋值) vs RHS(取值)
    • LHS 找不到变量时,非严格模式会创建全局变量(危险!)。
    • RHS 找不到变量时,直接报错。
  4. undefined vs ReferenceError
    • undefined → 声明但未赋值。
    • ReferenceError → 变量未声明。
  5. 词法作用域在代码编写时就确定了,和调用位置无关。

🎯 最后

理解作用域是JS进阶的核心基础,希望这篇文章能帮你彻底掌握它!

如果你觉得有用,点赞 + 收藏支持一下!🔥
关注我,解锁更多前端硬核知识!🚀

评论区互动
👉 你在写代码时,遇到过哪些作用域相关的问题?
👉 你觉得 varlet 哪个更好?为什么?

我们下期见!💪