JavaScript 的作用域
JavaScript 作用域完整分类
1. 按作用域类型分类
(1)全局作用域 (Global Scope)
- 定义:在任何函数、代码块之外声明的变量
- 生命周期:从脚本加载到页面关闭
- 访问性:在任何地方都可访问
- 示例:
var globalVar = "我在全局作用域";
let globalLet = "我也是全局的";
const globalConst = "我也是";function test() {console.log(globalVar); // 可以访问
}
(2)函数作用域 (Function Scope)
- 定义:在函数内部声明的变量(使用
var
、function
) - 生命周期:函数执行期间存在,执行结束被销毁
- 访问性:只能在函数内部访问
- 示例:
function myFunction() {var functionScoped = "我在函数作用域内";if (true) {var stillInFunction = "我还在函数作用域"; // 不是块级作用域!}console.log(stillInFunction); // 可以访问
}
// console.log(functionScoped); // 报错:未定义
(3)块级作用域 (Block Scope)
- 定义:由
{}
包围的代码块(if
、for
、while
、switch
等) - 关键:使用
let
和const
声明 - 示例:
if (true) {let blockScopedLet = "我在块级作用域";const blockScopedConst = "我也是";var notBlockScoped = "我逃出了块级作用域"; // var 没有块级作用域
}
console.log(notBlockScoped); // 可以访问
// console.log(blockScopedLet); // 报错:未定义
(4)模块作用域 (Module Scope)
- 定义:ES6 模块 (
<script type="module">
) 中的顶级作用域 - 特点:每个模块都有自己的作用域,不会污染全局
- 示例:
// module.js
const moduleVar = "我是模块作用域变量"; // 不是全局变量!
export function getVar() { return moduleVar; }// main.js
import { getVar } from './module.js';
console.log(getVar()); // 可以访问
// console.log(moduleVar); // 报错:未定义
(5)词法作用域 (Lexical Scope) - 静态作用域
- 定义:这不是一种新的作用域类型,而是作用域的工作规则
- 核心:函数的作用域在函数定义时就确定了,而不是在调用时
- 示例:
let global = "全局";function outer() {let outerVar = "外部";function inner() {console.log(outerVar); // "外部" - 根据定义位置查找console.log(global); // "全局" - 继续向外查找}return inner;
}const innerFunc = outer();
innerFunc(); // 即使在外面调用,仍能访问定义时的作用域
(6)eval 作用域 (Eval Scope) - 特殊情况
- 定义:
eval
函数执行时创建的临时作用域 - 行为复杂:在严格模式下有自己的作用域,非严格模式下会影响当前作用域
- 示例:
function testEval() {var normalVar = "正常变量";eval('var evalVar = "eval变量";');console.log(evalVar); // 非严格模式:可以访问
}// 严格模式下
function strictEval() {'use strict';eval('var strictEvalVar = "严格模式eval";');// console.log(strictEvalVar); // 报错:未定义
}
(7)with 作用域 (With Scope) - 已废弃
- 定义:
with
语句会将指定对象添加到作用域链前端 - 现状:严格模式禁止使用,性能差且易出错
- 示例:
var obj = { a: 1, b: 2 };
with(obj) {console.log(a + b); // 3 - a和b从obj中查找
}
2. 作用域链 (Scope Chain) 机制
作用域之间不是孤立的,它们通过作用域链连接:
// 作用域链示例
let globalVar = "全局";function outer() {let outerVar = "外部";function inner() {let innerVar = "内部";console.log(innerVar); // 当前作用域console.log(outerVar); // 外层函数作用域 console.log(globalVar); // 全局作用域}inner();
}outer();
作用域链查找顺序:当前作用域 → 外层函数作用域 → 更外层… → 全局作用域
3. 特殊情况和细节
暂时性死区 (Temporal Dead Zone - TDZ)
console.log(normalVar); // undefined (变量提升)
var normalVar = "var";// console.log(letVar); // 报错:不能在初始化前访问
let letVar = "let";
闭包 (Closure) - 作用域的延伸
function createCounter() {let count = 0; // 函数作用域变量return function() {count++; // 闭包:内部函数可以访问外部函数变量return count;};
}const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2 - count 变量仍然存在!
4. 总结表格
作用域类型 | 创建方式 | 变量声明 | 特点 |
---|---|---|---|
全局作用域 | 脚本最外层 | var , let , const | 随处可访问,生命周期最长 |
函数作用域 | 函数声明 | var , function | var 会变量提升 |
块级作用域 | {} 代码块 | let , const | 有暂时性死区,不会变量提升 |
模块作用域 | ES6 Module | 所有声明 | 文件级隔离,需要导出导入 |
词法作用域 | 代码书写位置决定 | - | 静态确定,闭包的基础 |
JavaScript 的作用域主要就是这些。理解它们的关键在于:
- 作用域是代码中变量的可访问范围
- 作用域在代码编写时(词法阶段)就确定了
- 作用域链决定了变量的查找顺序
- 不同的声明方式(var/let/const)在不同作用域中有不同行为