作用域
作用域(Scope) 是指变量、函数和对象在代码中可被访问的范围,它决定了代码中不同部分对标识符的访问权限
全局作用域
全局作用域(Global Scope) 是指代码中所有变量、函数和对象都存在的环境。全局作用域的变量、函数和对象可以在任何地方被访问。
let globalVar = 'I am global';
function globalFunc() {
console.log(globalVar);
}
globalFunc();// I am global局部作用域
局部作用域(Local Scope) 是指代码中某个函数或块级作用域中的变量、函数和对象。局部作用域的变量、函数和对象只能在该作用域内被访问。
函数作用域
函数作用域(Function Scope) 是指函数中的变量、函数和对象。函数作用域的变量、函数和对象只能在函数内部被访问。
function localFunc() {
let localVar = 'I am local';
console.log(localVar);
}
localFunc();// I am local
console.log(localVar);// ReferenceError: localVar is not defined块级作用域
块级作用域(Block Scope) 是指代码中某个块级作用域({})中的变量、函数和对象。块级作用域的变量、函数和对象只能在该作用域内被访问。
{
let blockVar = 'I am block';
var blockVar2 = 'I am block2';
function blockFunc() {
console.log(blockVar);
}
blockFunc();
}
console.log(blockVar);// ReferenceError: blockVar is not defined
console.log(blockVar2);// I am block2 var无块级作用域
blockFunc() // ReferenceError: blockFunc is not defined模块作用域
ES6 模块(import/export)中,变量默认属于模块作用域,外部无法访问,需通过 export 暴露。每个模块是独立的作用域,避免全局污染。
// module.js
let moduleVar = "模块内变量";
export let exposedVar = "对外暴露的变量";
// main.js
import {exposedVar} from "./module.js";
console.log(exposedVar); // 能访问:对外暴露的变量
console.log(moduleVar); // 报错:moduleVar is not defined作用域链
作用域查找
当访问一个变量时,JS 引擎会按以下顺序查找:
1.当前作用域 → 2.外层函数作用域... → 3. 全局作用域 → 4. 若未找到则返回 undefined(若赋值则隐式创建全局变量,严格模式下报错)。
这个查找链条就是作用域链,作用域链的顶端是全局作用域,底端是当前执行的作用域。
let global = "全局";
function fn1() {
let fn1Var = "fn1作用域";
function fn2() {
let fn2Var = "fn2作用域";
console.log(fn2Var, fn1Var, global);
}
fn2();
}
fn1(); // 输出:fn2作用域 fn1作用域 全局词法环境
词法环境(Lexical Environment) 是指作用域链的集合,每个作用域都有自己的词法环境,词法环境保存了作用域中的变量、函数和对象。
提示
作用域链的底层是 词法作用域(静态作用域) —— 作用域由代码书写时的嵌套结构决定,而非运行时的调用位置。
var globalVar = "全局变量";
function outer() {
var outerVar = "外层变量";
function inner() {
var innerVar = "内层变量";
console.log(innerVar); // 内层变量(当前作用域)
console.log(outerVar); // 外层变量(上一级作用域)
console.log(globalVar); // 全局变量(顶层作用域)
}
inner();
}
outer();单向性
只能向上查找,无法向下查找
function outer() {
var outerVar = "外层";
function inner() {
var innerVar = "内层";
}
console.log(innerVar); // ReferenceError: innerVar is not defined
}
outer();闭包
闭包的本质是函数保留了对外层作用域链的引用,即使外层函数执行完毕,内层函数仍能访问外层变量。
function createCounter() {
var count = 0; // 外层变量,被闭包引用
return function () {
count++;
return count;
};
}
var counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
// 此时createCounter已执行完毕,但counter的作用域链仍保留对count的引用变量提升
变量提升仅作用于当前作用域,查找时仍沿作用域链向上。
var a = 1;
function fn() {
console.log(a); // undefined(当前作用域的a被提升,未赋值)
var a = 2;
}
fn();fn 的作用域中先创建 a(值为 undefined),因此不会查找全局的 a。
误区
作用域链和原型链
作用域链和原型链是两个不同的概念,它们之间没有关系:
- 作用域链是 JS 引擎在运行时查找变量和函数的机制
- 原型链是 JS 对象的继承机制。
原型链
Object.prototype.a = 1
const obj1 = {}
const obj2 = Object.create(obj1)
console.log(obj.a)// 1
console.log(obj2.a)// 1 obj2-->obj1--->Object.prototype--->null认为作用域由调用顺序决定
函数调用顺序和作用域无关,作用域由代码书写时的嵌套结构决定,而非运行时的调用位置。
function fn1() {
console.log(a);
}
function fn2() {
var a = 2;
fn1(); // 1(fn1的作用域链是自身 → 全局,而非fn2)
}
var a = 1;
fn2();