JavaScript 核心机制

作用域 &
作用域链

理解作用域是掌握 JavaScript 的关键一步。它决定了变量的可访问性、 生命周期,以及代码的执行逻辑。让我们一起深入探索这个核心概念。

全局作用域
var globalVar = "全局";function outer() {...}
outer() 作用域
var outerVar = "外层";function inner() {...}
inner() 作用域
let innerVar = "内层";
全局
函数
块级
核心概念

三种作用域类型

Global Scope

全局作用域

在代码最外层声明的变量拥有全局作用域。它们可以在代码的任何位置被访问。

var name = "全局变量"
// 在任何地方都能访问
console.log(name) // ✅

注意:过多的全局变量会导致命名污染,应尽量避免。

Function Scope

函数作用域

在函数内部声明的变量只能在该函数内部访问,函数外部无法直接访问。

function foo() {
  var secret = "私有的"
}
console.log(secret) // ❌ Error

特性:var 声明的变量具有函数作用域,会进行变量提升。

Block Scope

块级作用域

ES6 引入的 let 和 const 具有块级作用域,只在最近的花括号内有效。

if (true) {
  let x = 10
  const PI = 3.14
}
console.log(x) // ❌ Error

优势:不存在变量提升的暂时性死区,更安全可控。

交互演示

作用域链如何查找变量

当访问一个变量时,JavaScript 引擎会从当前作用域开始,逐级向外层查找, 直到找到该变量或到达全局作用域。

示例代码
// ① 全局作用域
var globalVar = "全局"

// ② outer 函数作用域
function outer() {
  var outerVar = "外层"

  // ③ inner 函数作用域
  function inner() {
    let innerVar = "内层"
    // ④ 查找变量
    console.log(globalVar)
  }
  inner()
}
outer()
查找过程
1inner 作用域

查找 globalVar → 未找到

2outer 作用域

查找 globalVar → 未找到

3全局作用域

查找 globalVar → ✅ 找到!

查找完成!

在全局作用域中找到了 globalVar,返回值 "全局"

核心规则

作用域链的查找规则

由内向外

变量查找从当前作用域开始,逐级向上层作用域查找,直到全局作用域。

就近原则

如果同名变量在多个作用域中存在,使用离当前作用域最近的那个。

报错机制

如果一直查找到全局作用域都没有找到,会抛出 ReferenceError。

进阶知识

闭包:作用域的
延续与记忆

闭包(Closure)是 JavaScript 最强大的特性之一。当一个函数能够记住并访问它的词法作用域, 即使函数在该作用域之外执行,就产生了闭包。

闭包的本质是:内部函数持有对外部函数变量的引用, 使得这些变量不会被垃圾回收机制清除。

实际应用场景
  • 数据封装与私有变量
  • 函数柯里化(Currying)
  • 模块模式(Module Pattern)
  • 事件处理与回调函数
闭包示例
function createCounter() {
  let count = 0 // 私有变量

  return {
    increment() {
      return ++count
    },
    getCount() {
      return count
    }
  }
}

const counter = createCounter()
counter.increment() // 1
counter.increment() // 2
counter.getCount() // 2
// count 无法从外部直接访问
// console.log(count) ❌
外部函数
创建作用域
内部函数
持有引用
知识检验

挑战你的理解

通过这些经典面试题,测试你对作用域和作用域链的掌握程度。

1

以下代码输出什么?

var x = 10;
function foo() {
  console.log(x);
  var x = 20;
  console.log(x);
}
foo();
2

这段代码的输出是?

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
3

用 let 改写后输出什么?

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}

核心要点回顾

🎯

三种作用域

全局、函数、块级

🔗

作用域链

由内向外逐级查找

🔒

闭包机制

记住并访问词法环境

理解作用域和作用域链是编写高质量 JavaScript 代码的基础。 掌握这些概念,你就能更好地理解变量的行为、避免常见陷阱,并编写出更加优雅和可维护的代码。