在 JavaScript 中,作用域 决定了变量在程序中的可访问范围。理解作用域机制,是避免变量污染、命名冲突和“意外行为”的关键,也是写出清晰、安全代码的基础。
掌握全局作用域与局部作用域的区别
理解 var、let、const 在作用域上的差异
深入理解块级作用域与词法作用域(闭包基础)
了解 ES6 模块作用域的特性
避免因作用域不清导致的常见 bug
在任何函数或代码块外部声明的变量,属于全局作用域,可在程序任何地方访问。
// 全局变量
const companyName = "TechCorp";
function greet() {
console.log(companyName); // 可访问全局变量
}
greet(); // 输出:TechCorp风险:过多全局变量会导致:
命名冲突
意外修改
难以调试(“谁改了我的变量?”)
在函数内部声明的变量,仅在该函数内有效,外部无法访问。
function sayHello() {
let user = "Alice"; // 局部变量
console.log(user);
}
sayHello(); // 输出:Alice
// console.log(user); // 报错:user is not defined优点:封装性强,避免污染全局环境。
注意:在 ES6 之前,JavaScript 只有函数作用域(通过
var实现),没有真正的块级作用域。
var、let、const示例:var 无块级作用域
{
var x = 10; // var 不受 {} 限制
let y = 20;
const z = 30;
console.log(x, y, z); // 10 20 30
}
console.log(x); // 10(var 可在块外访问)
// console.log(y); // ReferenceError
// console.log(z); // ReferenceError关键结论:
let和const具有块级作用域(由{}定义)
var只有函数作用域或全局作用域
由 {} 包裹的代码区域(如 if、for、while、独立代码块等)构成一个块作用域。
if (true) {
let message = "Hello";
const version = "1.0";
var legacy = "old"; // 不受块限制
}
// console.log(message); // 错误
// console.log(version); // 错误
console.log(legacy); // "old"优势:
更精细的变量控制
避免循环变量泄漏(见下例)
经典问题:var 在循环中的陷阱
// 使用 var(有问题)
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// 输出:3 3 3(因为 i 是全局的,循环结束后 i=3)
// 使用 let(正确)
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 100);
}
// 输出:0 1 2(每次迭代创建新的 j)最佳实践:优先使用
let和const,避免var
也称为静态作用域,指函数的作用域在定义时就已确定,而非调用时。
function outer() {
const x = 10;
function inner() {
const y = 20;
console.log(x, y); // 可访问 outer 的 x
}
inner();
}
outer(); // 输出:10 20原理:
inner 函数在其定义位置“记住”了 outer 的作用域
这种机制是 闭包 的基础
词法作用域 = 函数能访问其外层函数中定义的变量。
ES6 引入了模块系统,每个 .js 文件默认是一个独立模块,拥有自己的作用域。
// math.js
const PI = 3.14159; // 私有变量,外部不可见
export function area(r) {
return PI * r * r;
}
// main.js
import { area } from './math.js';
console.log(area(2)); // 12.566...
// console.log(PI); // 无法访问特点:
模块内所有声明默认私有
必须通过 export 显式导出
通过 import 导入其他模块内容
彻底解决全局污染问题
注意:模块需在
<script type="module">或构建工具中使用。
永远不要使用 var,改用 let(可变)或 const(不可变)
优先使用 const,除非明确需要重新赋值
避免创建不必要的全局变量
利用块级作用域限制变量生命周期
使用 ES6 模块组织代码,实现天然作用域隔离
以下代码输出什么?为什么?
console.log(a); // ?
console.log(b); // ?
var a = 1;
let b = 2;如何修复下面的代码,使其输出 0, 1, 2?
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}为什么说“词法作用域”是闭包的基础?请用代码说明。