一、作用域(Scope)
作用域指的是变量和函数在代码中可访问的范围,它决定了代码中哪些部分可以读取或修改某个变量。其核心作用是:
- 隔离变量,避免命名冲突(不同作用域中可以有同名变量)
- 控制变量的生命周期(变量何时创建、何时销毁)
1. 前端常见的作用域类型
根据 ECMAScript 标准,前端主要有以下几种作用域:
(1)全局作用域(Global Scope)
-
定义:不在任何函数或块级结构内声明的变量 / 函数,属于全局作用域。
-
特点:在代码的任何地方都能访问,生命周期与页面一致(页面关闭时销毁)。
-
示例:
javascript// 全局变量 const globalVar = "我是全局变量"; function test() { console.log(globalVar); // 可访问全局变量 }
(2)函数作用域(Function Scope)
-
定义:在函数内部声明的变量 / 函数,仅在当前函数内部可访问。
-
特点:函数执行时创建,执行结束后销毁(闭包除外),外部无法直接访问。
-
示例:
javascriptfunction fn() { const funcVar = "我是函数内变量"; console.log(funcVar); // 正常访问 } fn(); console.log(funcVar); // 报错:funcVar is not defined
(3)块级作用域(Block Scope)
-
定义 :由
{}
包裹的代码块(如if
、for
、while
、switch
或单独的{}
)中,通过let
/const
声明的变量,仅在当前块内可访问。 -
特点 :
var
声明的变量不具备块级作用域(会 "变量提升" 到函数或全局作用域),而let
/const
支持块级作用域。 -
示例:
iniif (true) { let blockVar = "我是块级变量"; var notBlockVar = "我不受块级限制"; console.log(blockVar); // 正常访问 } console.log(blockVar); // 报错:blockVar is not defined console.log(notBlockVar); // 正常访问(var 声明会提升)
(4)模块作用域(Module Scope)
-
定义 :在 ES6 模块(
.mjs
或设置type="module"
的.js
文件)中声明的变量,默认仅在当前模块内可见。 -
特点 :需通过
export
导出,其他模块通过import
导入后才能访问,避免全局污染。 -
示例:
javascript// module.js const moduleVar = "我是模块内变量"; export default moduleVar; // app.js import moduleVar from './module.js'; console.log(moduleVar); // 正常访问
二、作用域链(Scope Chain)
当代码在某个作用域中访问一个变量时,JavaScript 引擎会先在当前作用域中查找该变量。如果找不到,会向上级作用域查找,直到全局作用域 ------ 这个由多层作用域组成的查找链条,就是作用域链。
1. 作用域链的形成
作用域链的结构由函数定义时的位置决定(而非执行时),这一特性称为 "静态作用域" 或 "词法作用域"。
- 每个函数在创建时,会隐式记录其 "父级作用域"(即定义该函数的作用域)。
- 当函数执行时,会创建一个 "执行上下文",其作用域链由当前函数作用域 + 父级作用域链组成。
2. 查找规则示例
ini
const globalVar = "全局变量";
function outer() {
const outerVar = "外层变量";
function inner() {
const innerVar = "内层变量";
// 查找顺序:inner 作用域 → outer 作用域 → 全局作用域
console.log(innerVar); // 内层变量(当前作用域)
console.log(outerVar); // 外层变量(父级作用域)
console.log(globalVar); // 全局变量(顶级作用域)
}
inner();
}
outer();
3. 作用域链的特性
-
单向查找:只能从当前作用域向上级查找,不能反向查找(子作用域的变量对父作用域不可见)。
-
就近原则:如果多层作用域中有同名变量,优先使用距离当前作用域最近的变量。
iniconst name = "全局"; function fn() { const name = "函数内"; console.log(name); // 输出 "函数内"(优先使用当前作用域的变量) }
三、作用域与作用域链的实际应用
-
避免全局污染:通过函数或块级作用域封装变量,减少全局变量的使用。
-
闭包的实现:闭包正是利用作用域链,让内部函数保留对父级作用域变量的访问权(即使父函数已执行完毕)。
javascriptfunction createCounter() { let count = 0; // 父级作用域变量 return function() { count++; // 内部函数通过作用域链访问 count return count; }; } const counter = createCounter(); console.log(counter()); // 1(count 未被销毁)
-
模块化开发 :利用模块作用域隔离不同文件的变量,通过
export
/import
控制访问权限。
总结
- 作用域:控制变量的可见范围,分为全局、函数、块级、模块作用域。
- 作用域链:变量查找的链条,由函数定义时的嵌套关系决定,遵循 "静态作用域" 规则。
- 理解这两个概念是掌握 JavaScript 变量生命周期、闭包、模块化等高级特性的基础。