Skip to content

作用域链

JavaScript 的作用域链(Scope Chain)是理解变量查找机制的关键概念。作用域链是一个由多个变量对象组成的列表,用于解析标识符(如变量名、函数名等)。当一个变量被引用时,JavaScript 引擎会沿着作用域链从当前执行上下文的变量对象开始查找,如果找不到,则继续向上查找,直到找到该变量或到达全局作用域。

作用域链的工作原理

  1. 创建执行上下文:每当一个函数被调用时,JavaScript 引擎会创建一个新的执行上下文。每个执行上下文都有自己的变量对象(Variable Object, VO),这个对象包含了函数的参数、局部变量和内部函数。

  2. 建立作用域链:在创建执行上下文时,引擎还会建立一个作用域链。作用域链是一个包含多个变量对象的列表,这些变量对象按顺序排列,从当前执行上下文的变量对象开始,依次向上级作用域的变量对象延伸,最终到达全局变量对象。

  3. 变量查找:当一个变量被引用时,JavaScript 引擎会从当前执行上下文的变量对象开始查找。如果找不到,就继续沿着作用域链向上查找,直到找到该变量或到达全局作用域。如果在整个作用域链中都找不到该变量,就会抛出 ReferenceError

示例

以下是一些示例,展示了作用域链的工作原理:

基本示例

javascript
var globalVar = "I'm global";

function outer() {
  var outerVar = "I'm in the outer function";

  function inner() {
    var innerVar = "I'm in the inner function";
    console.log(globalVar); // 全局变量
    console.log(outerVar); // 外部函数的局部变量
    console.log(innerVar); // 内部函数的局部变量
  }

  inner();
}

outer();

在这个例子中:

  • inner 函数的作用域链包括 inner 函数的变量对象、outer 函数的变量对象和全局变量对象。
  • inner 函数中的 console.log(globalVar) 被执行时,globalVar 会在 inner 的变量对象中查找,找不到则继续向上查找,最终在全局变量对象中找到。
  • 同样地,outerVarinnerVar 也会在相应的作用域中找到。

闭包示例

javascript
function createCounter() {
  let count = 0;

  return function () {
    count++;
    console.log(count);
  };
}

const counter = createCounter();

counter(); // 输出: 1
counter(); // 输出: 2
counter(); // 输出: 3

在这个例子中:

  • createCounter 函数返回了一个匿名函数。
  • 这个匿名函数形成了一个闭包,因为它保留了对 count 变量的访问权。
  • 即使 createCounter 已经执行完毕并退出了,count 仍然存在于内存中,因为 counter 函数需要访问它。
  • counter 函数的作用域链包括其自身的变量对象、createCounter 函数的变量对象和全局变量对象。

作用域链的可视化

假设我们有以下代码:

javascript
var globalVar = "I'm global";

function outer() {
  var outerVar = "I'm in the outer function";

  function inner() {
    var innerVar = "I'm in the inner function";
    console.log(globalVar, outerVar, innerVar);
  }

  inner();
}

outer();

对应的作用域链可以表示为:

  • inner 函数的作用域链:
    1. inner 函数的变量对象(包含 innerVar
    2. outer 函数的变量对象(包含 outerVar
    3. 全局变量对象(包含 globalVar

总结

  • 作用域链:是一个由多个变量对象组成的列表,用于解析标识符。
  • 变量查找:从当前执行上下文的变量对象开始,沿着作用域链向上查找,直到找到该变量或到达全局作用域。
  • 闭包:如果一个函数返回另一个函数,并且这个内部函数引用了外部函数的局部变量,那么这些局部变量会被闭包捕获,即使外部函数已经执行完毕,这些变量仍然可以通过闭包访问。

理解作用域链对于编写高效、可维护的 JavaScript 代码非常重要。如果你有更多关于作用域链的具体问题或需要进一步的例子,请告诉我!

Released under the MIT License.