JavaScript作用域和作用域链的理解

57 阅读3分钟

在 JavaScript 里,作用域和作用域链是基础概念,它们决定了变量与函数的可访问范围。

作用域

作用域的定义是变量和函数的可访问范围,它控制着变量与函数的可见性和生命周期。在 JavaScript 中,作用域可以分为以下几种类型:

  1. 全局作用域:在代码里任何地方都能访问全局作用域中的变量。
var globalVar = '我处于全局作用域';
function test() {
    console.log(globalVar); // 能够访问全局变量
}
  1. 函数作用域:在函数内部定义的变量,只能在该函数内部被访问。
function example() {
    var localVar = '我处于函数作用域';
    console.log(localVar); // 可以访问局部变量
}
example();
console.log(localVar); // 会报错,无法访问局部变量
  1. 块级作用域(借助 let 和 const 实现):在 {} 内部定义的变量,只能在这个块内被访问。
if (true) {
    let blockVar = '我处于块级作用域';
    console.log(blockVar); // 可以访问块级变量
}
console.log(blockVar); // 会报错,无法访问块级变量

作用域链

当查找一个变量时,JavaScript 引擎会先在当前作用域中进行搜索,如果没找到,就会到父级作用域中继续搜索,直到找到该变量或者到达全局作用域。这种层层嵌套的作用域组合起来,就形成了作用域链。如果在全局作用域里仍然找不到该变量,它就会在全局范围内隐式声明该变量(非严格模式下)或是直接报错。

下面通过一个例子来理解作用域链:

var globalVar = '全局变量';
function outer() {
    var outerVar = '外层函数变量';
    function inner() {
        var innerVar = '内层函数变量';
        console.log(innerVar); // 访问自身作用域的变量
        console.log(outerVar); // 访问外层函数的变量
        console.log(globalVar); // 访问全局变量
    }
    inner();
}
outer();

作用域链的工作流程

  1. 当 inner 函数尝试访问 innerVar 时,会直接在自己的作用域中找到该变量。
  2. 当访问 outerVar 时,在自身作用域中找不到,就会到外层函数 outer 的作用域中去寻找。
  3. 当访问 globalVar 时,在内层和外层函数的作用域中都找不到,就会到全局作用域中寻找。

闭包与作用域链

闭包是指有权访问另一个函数作用域中的变量的函数。闭包会携带包含它的函数的作用域,因此可能会导致内存占用方面的问题。

function outer() {
    var count = 0;
    return function() {
        count++; // 闭包可以访问外层函数的变量
        console.log(count);
    };
}
var counter = outer();
counter(); // 输出 1
counter(); // 输出 2

在这个例子中,内部函数形成了一个闭包,它始终保留着对 outer 函数中 count 变量的引用。

总结

  • 作用域:规定了变量和函数的可见范围,有全局作用域、函数作用域和块级作用域之分。
  • 作用域链:是由多个嵌套的作用域组成的,它决定了变量的查找顺序。
  • 闭包:能够访问其他函数作用域中变量的函数,即使该函数已经执行完毕。