返回

手把手教你搞懂 JavaScript 中的执行上下文

前端

深入浅出:什么是 JavaScript 中的执行上下文(EC)?

在 JavaScript 中,当引擎解析到可执行代码时,就会创建对应的执行上下文 (execution context),也称为执行环境。这是一个抽象概念,表示代码执行的环境。乍听之下,它和词法作用域有点像,但两者截然不同。

词法作用域 是由代码结构决定的,而执行上下文 则是在代码执行时动态创建的。最大的区别在于,词法作用域规则是在代码编写时就已经确定的,而执行上下文是在代码执行时动态创建的。

EC 与词法作用域:理清它们的联系与区别

当我们谈论 EC 时,很容易与词法作用域混淆。然而,两者之间存在着本质区别。

词法作用域是一种静态概念,它在代码编写时就已经确定了。也就是说,一个变量或函数的作用域是由代码的结构决定的。而在 JavaScript 中,词法作用域只有两种类型:函数作用域块级作用域

另一方面,执行上下文则是一个动态的概念,它是代码执行时才创建的。当执行上下文被创建时,它会捕获当前的变量和函数。因此,EC 允许你在一个函数内访问另一个函数中的变量。

探索执行上下文的运作原理

EC 有两个主要组成部分:

  1. 变量环境 :存储着变量和函数的名称及其对应的值。
  2. 作用域链 :它是一个由当前执行上下文和所有父级执行上下文组成的链。作用域链用于查找变量和函数的定义。

变量环境 :它是一个对象,包含了在当前执行上下文中定义的所有变量和函数及其值。当我们进入一个新环境(例如函数或块)时,就会创建一个新的变量环境,该环境将继承父环境中的所有变量和函数。

作用域链 :作用域链是一个由当前执行上下文和所有父级执行上下文组成的链。当我们查找一个变量或函数时,引擎会沿着作用域链向上查找,直到找到该变量或函数的定义。

EC 与 this 揭开它们的微妙关系

this 在 JavaScript 中是一个非常重要的概念,它指向当前执行上下文的调用对象。

当一个函数作为对象的方法调用时,this 将指向该对象。例如,我们有如下代码:

const obj = {
  name: 'John',
  sayHello() {
    console.log(`Hello, my name is ${this.name}.`);
  },
};

obj.sayHello(); // Output: "Hello, my name is John."

在这个例子中,obj.sayHello() 被调用时,this 指向 obj 对象,因为 sayHello 方法是在 obj 对象的上下文中被调用的。

EC 与函数执行:厘清它们的协同工作方式

当一个函数被调用时,就会创建一个新的执行上下文。这个新的执行上下文将继承父执行上下文中的所有变量和函数。

例如,我们有如下代码:

function outer() {
  let name = 'John';

  function inner() {
    console.log(name); // Output: "John"
  }

  inner();
}

outer();

在这个例子中,当 outer 函数被调用时,就会创建一个新的执行上下文。这个新的执行上下文继承了 outer 函数的变量环境和作用域链。因此,当 inner 函数被调用时,它可以访问 outer 函数中的 name 变量。

掌握词法作用域规则,助你游刃有余

词法作用域规则规定了变量和函数的作用域。

function outer() {
  let name = 'John';

  function inner() {
    let name = 'Jane';
    console.log(name); // Output: "Jane"
  }

  inner();
}

outer();

在这个例子中,inner 函数有一个自己的 name 变量,因此它不会覆盖 outer 函数中的 name 变量。这就是词法作用域规则的作用。

巧用 with 语句,拓展作用域的边界

with 语句允许我们暂时改变执行上下文中的 this 关键字。它将 this 指向指定的表达式,而不是当前执行上下文中的调用对象。

例如,我们有如下代码:

const obj = {
  name: 'John',
  sayHello() {
    console.log(`Hello, my name is ${this.name}.`);
  },
};

with (obj) {
  sayHello(); // Output: "Hello, my name is John."
}

在这个例子中,with 语句将 this 指向 obj 对象,所以 sayHello 方法可以访问 obj 对象的 name 属性。

总结:融会贯通,运用自如

JavaScript 中的执行上下文是一个复杂但又重要的概念。理解 EC 可以帮助我们更好地理解 JavaScript 代码是如何执行的。掌握执行上下文,我们可以轻松处理词法作用域、this 关键字和函数执行等概念。