JavaScript 作用域

作用域(Scope)决定了在哪里可以访问变量。理解作用域是写出正确、无 bug 的 JavaScript 代码的基础。

三种作用域

作用域由…创建示例
全局作用域代码的最外层在所有函数外部声明的变量
函数作用域函数体 {}function 内部声明的变量
块级作用域{} 代码块ifforwhile{} 内部

全局作用域

在任何函数或代码块外部声明的变量是全局变量,在代码的任何地方都可访问:

javascript
const siteName = 'coodocs.com';

function showSite() {
  console.log(siteName); // 可以访问全局变量
}

showSite(); // "coodocs.com"

函数作用域

在函数内部声明的变量,只在该函数内部可见

javascript
function greet() {
  const message = '你好';
  console.log(message); // ✅ 函数内部可访问
}

greet();
console.log(message); // ❌ ReferenceError: message is not defined

每次调用函数都会创建一个独立的作用域,互不干扰:

javascript
function counter() {
  let count = 0;
  count++;
  console.log(count);
}

counter(); // 1
counter(); // 1(每次调用 count 都从 0 开始)
counter(); // 1

块级作用域(let / const

letconst 声明的变量仅在它们所在的花括号 {}可见:

javascript
if (true) {
  const x = 10;
  let y = 20;
  console.log(x, y); // ✅ 10 20
}
console.log(x); // ❌ ReferenceError: x is not defined
javascript
for (let i = 0; i < 3; i++) {
  console.log(i); // ✅ 0, 1, 2
}
console.log(i); // ❌ ReferenceError: i is not defined

var 声明的变量没有块级作用域——它在最近的函数或全局范围内可见。这就是为什么 var 被弃用:if (true) { var x = 10; } console.log(x); 不会报错,但这是一个反直觉的设计。

作用域链

当 JavaScript 查找一个变量时,从当前作用域开始,逐层向外查找,直到全局作用域。如果全局作用域也没有,则报错:

javascript
const global = '全局';

function outer() {
  const outerVar = '外层';

  function inner() {
    const innerVar = '内层';
    console.log(innerVar); // 在自己的作用域找到
    console.log(outerVar); // 在外层函数作用域找到
    console.log(global);   // 在全局作用域找到
  }

  inner();
}

outer();

这个从内到外的查找路径就叫作用域链

闭包(Closure)——入门

闭包是作用域的自然结果:一个函数”记住”了它被创建时的外部变量,即使这个函数在外部作用域之外被调用:

javascript
function createCounter() {
  let count = 0; // 这个变量被内部函数"捕获"
  return function() {
    count++;
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
// 每次调用 counter(),它都能访问并修改 createCounter 内的 count

这是 JavaScript 中最强大的模式之一,广泛应用于模块封装、数据私有化、回调函数等场景。闭包的深入内容在后续进阶章节中探讨。

作用域最佳实践

  • 优先使用 const,需要重新赋值时用 let
  • 避免全局变量——过多的全局变量会污染命名空间,增加意外冲突的风险
  • 将变量声明在最窄的作用域——如果变量只在 if 块内使用,就声明在该块内
  • 函数不宜过深嵌套——作用域链过长会让代码难以理解和维护
javascript
// ✅ 好的实践
function processUser(user) {
  if (!user) return;

  const name = user.name; // 声明在需要的最外层作用域

  if (user.isAdmin) {
    const adminMessage = '管理员'; // 仅在此块内需要
    console.log(`${name} - ${adminMessage}`);
  }
}