前端JS: 执行上下文

1 阅读3分钟

JavaScript 执行上下文是代码被评估和执行时所在的环境,它是 JavaScript 核心的运行机制。

简单来说,当一段 JavaScript 代码运行时,它发生在一个“执行上下文”中。这个上下文决定了变量、函数、作用域和 this的值。

执行上下文的类型

主要有三种:

  1. 全局执行上下文

    • 这是默认的、最外层的上下文。
    • 不在任何函数内部的代码都在这里执行。
    • 在浏览器中,全局上下文关联着 window对象。一个程序中只有一个全局执行上下文。
  2. 函数执行上下文

    • 每次调用函数时,都会为该函数创建一个全新的执行上下文。
    • 即使调用同一个函数多次,每次调用也会创建独立的新上下文。
  3. Eval 函数执行上下文

    • eval函数内部执行的代码也有自己的执行上下文(由于安全性和性能问题,通常不推荐使用 eval)。

执行上下文的生命周期

每个执行上下文都会经历两个阶段:

1. 创建阶段

在代码执行之前,执行上下文会被创建。此时会做以下几件事:

  • 创建变量对象 (VO)

    • 函数参数:建立arguments对象(在非箭头函数中)。
    • 函数声明:扫描并“提升”整个函数。
    • 变量声明:扫描并“提升”变量,但初始值为 undefined
  • 建立作用域链 (Scope Chain) :由当前上下文和所有父级上下文的变量对象组成,决定了标识符(变量、函数)的查找顺序。

  • 确定 this的值

2. 执行阶段

开始顺序执行代码,为变量赋值,并执行函数调用。

执行栈(调用栈)

JavaScript 引擎使用一个“栈”(后进先出)来管理所有的执行上下文。

  • 当程序开始运行时,首先将全局执行上下文压入栈底。
  • 每当一个函数被调用,就为该函数创建一个新的函数执行上下文,并将其压入栈顶。
  • 当前正在执行的函数上下文永远在栈顶。
  • 当函数执行完毕,其上下文会从栈顶弹出,控制权交还给栈中的下一个上下文。

示例

let globalVar = ‘I am global’;

function outer() {
  let outerVar = ‘I am outer’;

  function inner() {
    let innerVar = ‘I am inner’;
    console.log(innerVar); // 查找顺序:当前上下文 -> outer上下文 -> 全局上下文
    console.log(outerVar);
    console.log(globalVar);
  }
  inner();
}
outer();

执行栈的变化

  1. 脚本开始:[全局上下文]
  2. 调用 outer()[全局上下文, outer上下文]
  3. outer中调用 inner()[全局上下文, outer上下文, inner上下文]
  4. inner执行完毕,弹出:[全局上下文, outer上下文]
  5. outer执行完毕,弹出:[全局上下文]

总结:理解执行上下文是理解 JavaScript 核心机制(如作用域、闭包、this绑定、变量提升)的基础。它描述了代码在运行时所处的环境及其可访问的资源。