普通视图

发现新文章,点击刷新页面。
今天 — 2025年5月18日首页

JavaScript执行栈和执行上下文

2025年5月18日 16:16

在JavaScript中,执行栈和执行上下文是理解代码执行流程和作用域链的关键概念。它们决定了代码如何执行以及变量和函数如何被查找和访问。本文将详细介绍执行上下文的生命周期、执行栈的工作原理以及它们在实际编程中的应用。

一、执行上下文

(一)什么是执行上下文?

执行上下文(Execution Context)是JavaScript代码执行的环境。它是一个抽象的概念,用于描述代码在运行时的状态。每当JavaScript代码运行时,它都在某个执行上下文中运行。

(二)执行上下文的类型

JavaScript中有三种执行上下文类型:

  1. 全局执行上下文:这是默认的上下文,任何不在函数内部的代码都在全局上下文中运行。全局执行上下文在页面加载时创建,当页面关闭时销毁。
  2. 函数执行上下文:每当一个函数被调用时,都会为该函数创建一个新的执行上下文。函数执行上下文在函数调用时创建,函数执行完成后销毁。
  3. eval函数执行上下文eval函数内部的代码也有自己的执行上下文。不过,eval函数的使用并不推荐,因为它会带来安全问题和性能问题。

(三)执行上下文的生命周期

执行上下文的生命周期分为两个阶段:

  1. 创建阶段:当代码执行进入一个环境时,会创建一个执行上下文。在这个阶段,执行上下文会进行以下操作:

    • 创建变量对象(Variable Object,VO):包括函数的形参、arguments对象、函数声明和变量声明。
    • 确定this的指向。
    • 确定作用域链。
  2. 执行阶段:在执行阶段,代码开始执行,变量被赋值,函数被调用,其他代码按顺序执行。

二、执行栈

(一)什么是执行栈?

执行栈(Call Stack)是JavaScript运行时用来管理执行上下文的一种数据结构。它是一个后进先出(LIFO)的栈结构,用于跟踪函数调用的顺序。

(二)执行栈的工作原理

  1. 入栈:当代码执行进入一个新的环境时,对应的执行上下文会被推入执行栈中。
  2. 出栈:当函数执行完成时,对应的执行上下文会被从执行栈中弹出,控制权交由下一个执行上下文。

(三)执行栈的特点

  • 后进先出:最后进入执行栈的执行上下文最先被弹出。
  • 栈顶是当前执行的上下文:执行栈的栈顶总是当前正在执行的函数的执行上下文。

(四)执行栈的图解

以下是一个具体的代码示例及其对应的执行栈图解:

function foo() { 
    function bar() {        
        return 'I am bar';
    }
    return bar();
}
foo();

对应的执行栈图解如下:

执行栈图解

(五)执行栈的数量限制

虽然执行上下文的数量没有明确的限制,但如果超出栈分配的空间,会造成堆栈溢出。常见于递归调用,没有终止条件造成死循环的场景。

// 递归调用自身
function foo() {
    foo();
}
foo();
// 报错:Uncaught RangeError: Maximum call stack size exceeded

三、执行上下文的生命周期

(一)创建阶段

在创建阶段,执行上下文会进行以下操作:

  1. 创建变量对象(VO)

    • 确定函数的形参(并赋值)。
    • 初始化arguments对象(并赋值)。
    • 确定普通字面量形式的函数声明(并赋值)。
    • 变量声明,函数表达式声明(未赋值)。
  2. 确定this的指向this的值由调用者决定。

  3. 确定作用域:由词法环境决定,哪里声明定义,就在哪里确定。

(二)执行阶段

在执行阶段,执行上下文会进行以下操作:

  1. 变量对象赋值

    • 变量赋值。
    • 函数表达式赋值。
  2. 调用函数

  3. 顺序执行其他代码

四、变量对象

变量对象(Variable Object,VO)是执行上下文的一个重要组成部分,它是一个包含变量、函数声明和形参的对象。在创建阶段,变量对象会被初始化,包括以下内容:

  • arguments对象:包含函数调用时传入的参数。
  • 形参:函数的形参会被赋值。
  • 函数声明:函数声明会被提升并赋值。
  • 变量声明:变量声明会被提升,但未赋值。

(一)变量对象的示例

以下是一个具体的代码示例及其对应的变量对象:

const foo = function(i) {
    var a = "Hello";
    var b = function privateB() {};
    function c() {}
}
foo(10);

在创建阶段,变量对象如下:

fooExecutionContext = {
    variableObject: {
        arguments: {0: 10, length: 1}, // 确定Arguments对象
        i: 10, // 确定形参
        c: pointer to function c(), // 确定函数引用
        a: undefined, // 局部变量初始值为undefined
        b: undefined // 局部变量初始值为undefined
    },
    scopeChain: {},
    this: {}
}

在执行阶段,变量对象如下:

fooExecutionContext = {
    variableObject: {
        arguments: {0: 10, length: 1},
        i: 10,
        c: pointer to function c(),
        a: "Hello", // a变量被赋值为Hello
        b: pointer to function privateB() // b变量被赋值为privateB()函数
    },
    scopeChain: {},
    this: {}
}

五、总结

执行上下文和执行栈是JavaScript中非常重要的概念。理解它们的工作原理和生命周期,可以帮助你更好地理解代码的执行流程和作用域链。

昨天 — 2025年5月17日首页
❌
❌