Skip to content

作用域

作用域(Scope) 是指变量、函数和对象在代码中可被访问的范围,它决定了代码中不同部分对标识符的访问权限

全局作用域

全局作用域(Global Scope) 是指代码中所有变量、函数和对象都存在的环境。全局作用域的变量、函数和对象可以在任何地方被访问。

js
let globalVar = 'I am global';

function globalFunc() {
    console.log(globalVar);
}

globalFunc();// I am global

局部作用域

局部作用域(Local Scope) 是指代码中某个函数或块级作用域中的变量、函数和对象。局部作用域的变量、函数和对象只能在该作用域内被访问。

函数作用域

函数作用域(Function Scope) 是指函数中的变量、函数和对象。函数作用域的变量、函数和对象只能在函数内部被访问。

js
function localFunc() {
    let localVar = 'I am local';
    console.log(localVar);
}

localFunc();// I am local
console.log(localVar);// ReferenceError: localVar is not defined

块级作用域

块级作用域(Block Scope) 是指代码中某个块级作用域({})中的变量、函数和对象。块级作用域的变量、函数和对象只能在该作用域内被访问。

js
{
    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 暴露。每个模块是独立的作用域,避免全局污染。

js
// 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(若赋值则隐式创建全局变量,严格模式下报错)。

这个查找链条就是作用域链,作用域链的顶端是全局作用域,底端是当前执行的作用域。

js
let global = "全局";

function fn1() {
    let fn1Var = "fn1作用域";

    function fn2() {
        let fn2Var = "fn2作用域";
        console.log(fn2Var, fn1Var, global);
    }

    fn2();
}

fn1(); // 输出:fn2作用域 fn1作用域 全局

词法环境

词法环境(Lexical Environment) 是指作用域链的集合,每个作用域都有自己的词法环境,词法环境保存了作用域中的变量、函数和对象。

提示

作用域链的底层是 词法作用域(静态作用域) —— 作用域由代码书写时的嵌套结构决定,而非运行时的调用位置。

js
var globalVar = "全局变量";

function outer() {
    var outerVar = "外层变量";

    function inner() {
        var innerVar = "内层变量";
        console.log(innerVar);  // 内层变量(当前作用域)
        console.log(outerVar);  // 外层变量(上一级作用域)
        console.log(globalVar); // 全局变量(顶层作用域)
    }

    inner();
}

outer();

单向性

只能向上查找,无法向下查找

js
function outer() {
    var outerVar = "外层";

    function inner() {
        var innerVar = "内层";
    }

    console.log(innerVar); // ReferenceError: innerVar is not defined
}

outer();

闭包

闭包的本质是函数保留了对外层作用域链的引用,即使外层函数执行完毕,内层函数仍能访问外层变量。

js
function createCounter() {
    var count = 0; // 外层变量,被闭包引用
    return function () {
        count++;
        return count;
    };
}

var counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
// 此时createCounter已执行完毕,但counter的作用域链仍保留对count的引用

变量提升

变量提升仅作用于当前作用域,查找时仍沿作用域链向上。

js
var a = 1;

function fn() {
    console.log(a); // undefined(当前作用域的a被提升,未赋值)
    var a = 2;
}

fn();

fn 的作用域中先创建 a(值为 undefined),因此不会查找全局的 a。

误区

作用域链和原型链

作用域链和原型链是两个不同的概念,它们之间没有关系:

  • 作用域链是 JS 引擎在运行时查找变量和函数的机制
  • 原型链是 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

认为作用域由调用顺序决定

函数调用顺序和作用域无关,作用域由代码书写时的嵌套结构决定,而非运行时的调用位置。

js
function fn1() {
    console.log(a);
}

function fn2() {
    var a = 2;
    fn1(); // 1(fn1的作用域链是自身 → 全局,而非fn2)
}

var a = 1;
fn2();

这是我的个人文档