前言
JavaScript 的作用域和作用域链就像代码世界的"交通规则"🚦,它们决定了变量在哪里能被访问、如何被查找。理解这些概念能让你写出更清晰、更健壮的代码!本文将通过生动的解释和实例,带你轻松掌握这些核心知识点。
一、作用域:变量的"活动范围"📍
1. 什么是作用域?
作用域就是变量的"生活区域"🏠,它决定了:
-
变量能被访问的范围
-
相同名字的变量在不同区域互不干扰
核心规则:内层作用域可以访问外层变量,外层不能访问内层变量
2. 作用域的类型
🌍 全局作用域:随处可访问
以下情况会创建全局作用域变量:
// 1. 最外层声明的变量
var globalVar = "我是全局变量";
// 2. 未声明直接赋值的变量
function createGlobal() {
accidentalGlobal = "糟糕,我成全局变量了!";
}
createGlobal();
console.log(accidentalGlobal); // ✅ 输出成功
// 3. 浏览器中的window属性 / Node中的global属性
globalThis.globalProp = "我也是全局的";
console.log(globalProp); // ✅ 输出成功
⚠️ var 的变量提升小秘密:
console.log(hoisted); // undefined ✅ (不会报错)
var hoisted = "我被提升了!";
🏠 函数作用域:函数内的私人空间
var outer = "外部变量";
function myFunction() {
var inner = "内部变量";
console.log(outer); // ✅ "外部变量"
console.log(inner); // ✅ "内部变量"
}
myFunction();
console.log(inner); // ❌ 报错:inner未定义
🧱 块级作用域:ES6的新特性
通过 let 和 const 创建的花括号 {} 作用域:
{
let blockScoped = "我在块内";
var notBlockScoped = "我溜出去了";
console.log(blockScoped); // ✅
}
console.log(notBlockScoped); // ✅
console.log(blockScoped); // ❌ 报错!
var vs let/const 大比拼
| 特性 | var | let/const |
|---|---|---|
| 变量提升 | ✅ 可提前访问 | ❌ 会报错(TDZ) |
| 重复声明 | ✅ 允许 | ❌ 禁止 |
| 挂载全局对象 | ✅ 是全局对象属性 | ❌ 不是 |
| 作用域类型 | 全局/函数作用域 | 块级作用域 |
const vs let 小区别
| 特性 | const | let |
|---|---|---|
| 重新赋值 | ❌ 禁止 | ✅ 允许 |
| 初始化要求 | ✅ 必须声明时赋值 | ❌ 不需要 |
🔍
const的"不变"真相:
const myArray = [1, 2];
myArray.push(3); // ✅ 可以!修改内容没问题
myArray = [4, 5]; // ❌ 报错!不能重新赋值
const myObject = { name: "小明" };
myObject.age = 20; // ✅ 可以!
myObject = { name: "小红" }; // ❌ 报错!
二、作用域链:变量的"寻宝路线图"🗺️
当 JavaScript 需要找一个变量时:
-
先在当前作用域找
-
找不到就去上一层作用域找
-
一直找到全局作用域为止
-
如果全局都没有,就报错!
let globalTreasure = "全局宝藏";
function outerFunction() {
let outerTreasure = "外层宝藏";
function innerFunction() {
let innerTreasure = "内层宝藏";
console.log(innerTreasure); // ✅ 内层宝藏
console.log(outerTreasure); // ✅ 外层宝藏
console.log(globalTreasure); // ✅ 全局宝藏
}
innerFunction();
}
outerFunction();
作用域链的实际应用
let name = "全局名字";
function showName() {
let name = "函数内部名字";
console.log(name); // 先找当前作用域 → "函数内部名字"
}
showName();
console.log(name); // 全局作用域 → "全局名字"
let drink = "🍵茶";
function prepareDrink() {
console.log(drink); // 当前作用域没有 → 找外层 → "🍵茶"
}
prepareDrink(); // 输出 🍵茶
总结小贴士 💡
-
作用域是变量的"活动范围"
-
全局作用域随处可访问,但要小心污染
-
函数作用域保护内部变量
-
块级作用域用
let/const+{}创建 -
作用域链是从内到外的查找路径