847 字
4 分钟
JavaScript 作⽤域和作⽤域链
.png)
核心定义
作用域是变量/函数的可访问范围规则:
- 词法作用域(静态):作用域在代码编写时确定(ES5+主流)
- 包含:
- 全局作用域
- 函数作用域
- 块作用域(ES6 let/const) 作用域链是嵌套作用域的层级结构,当访问变量时,JS引擎沿链向上查找(当前作用域 → 父作用域 → 全局),直到找到或报错。
工作原理
- 词法分析阶段:根据代码嵌套关系确定作用域链
- 执行阶段:
- 访问变量时,从当前作用域开始查找
- 若未找到,向父级作用域递归查找
- 直到全局作用域(未找到则报错)
- 闭包机制:内部函数保留外部函数作用域链
flowchart TD A[访问变量 x] --> B{当前作用域有 x?} B -->|是| C[使用当前 x] B -->|否| D{有父作用域?} D -->|是| E[向父作用域查找] D -->|否| F[抛出 ReferenceError] E --> B
关键点
- 作用域类型:
- 全局:
window
(浏览器) - 函数:每次调用创建新作用域
- 块:
{}
内的let
/const
(ES6+)
- 全局:
- 查找方向:由内向外单向查找(内部可访问外部,反之不行)
- 变量遮蔽:内层同名变量覆盖外层
- 词法固定:作用域链在函数声明时确定(非调用时)
- 严格模式:
'use strict'
阻止意外创建全局变量
常见误区
- 混淆作用域与上下文:作用域是变量访问规则,上下文是
this
值 - 误判变量查找:认为
try/catch
的catch
块有独立作用域(ES3) - 忽略块作用域:在
if/for
中使用var
导致变量泄露 - 动态作用域误解:JS是词法作用域(非动态作用域如bash)
- 闭包引用混淆:认为闭包捕获变量值(实际捕获变量引用)
应用场景
场景 | 示例 | 作用域机制 |
---|---|---|
模块封装 | IIFE: (function() { ... })() | 创建私有作用域避免污染全局 |
闭包实现 | function outer() { let x; return function inner() { x++ } } | 内部函数保留外部作用域访问权 |
变量保护 | 块级作用域:{ let temp = ... } | 限制临时变量生命周期 |
命名冲突解决 | 函数作用域内定义同名变量 | 遮蔽外部变量避免冲突 |
递归函数 | function fib(n) { ... fib(n-1) ... } | 每次调用创建独立作用域 |
异步回调 | setTimeout(function() { ... }, 100) | 回调函数继承声明时作用域链 |
关联知识
- 闭包(Closure):函数携带其声明时作用域链
- 变量提升:
var
声明提升至函数/全局作用域顶部 - 执行上下文:包含作用域链、变量对象、
this
值 this
绑定:与作用域链无关,由调用方式决定- 内存管理:
- 作用域销毁时变量自动回收
- 闭包导致外部变量延迟回收
- 严格模式特性:
- 禁止未声明变量赋值
eval
使用独立作用域
- ES6 特性:
let
/const
的块级作用域- 暂时性死区(TDZ)
- 模块作用域(
import/export
)
黄金法则:
- 作用域 = 变量可见性规则(词法作用域)
- 作用域链 = 嵌套作用域的层级引用链
- 变量查找:当前作用域 → 父级作用域 → … → 全局作用域 使用 Chrome DevTools 的 Scope 面板实时查看作用域链!