JavaScript 作用域
作用域(Scope)决定了在哪里可以访问变量。理解作用域是写出正确、无 bug 的 JavaScript 代码的基础。
三种作用域
| 作用域 | 由…创建 | 示例 |
|---|---|---|
| 全局作用域 | 代码的最外层 | 在所有函数外部声明的变量 |
| 函数作用域 | 函数体 {} | function 内部声明的变量 |
| 块级作用域 | {} 代码块 | if、for、while 的 {} 内部 |
全局作用域
在任何函数或代码块外部声明的变量是全局变量,在代码的任何地方都可访问:
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)
let 和 const 声明的变量仅在它们所在的花括号 {} 内可见:
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}`);
}
}