详解JavaScript执行上下文与调用栈机制

作者:半夏

时间:2026-10-24

浏览:207

本文深入解析JavaScript中的执行上下文与调用栈机制。详细阐述全局、函数及Eval三种执行环境的区别,通过递归示例演示调用栈的先进后出特性。同时剖析执行上下文的创建阶段与执行阶段,解析变量对象、作用域链及this指向的生成过程,帮助开发者理解代码运行底层原理。

文章目录

一、执行上下文是什么

代码的运行依赖于特定的运行环境,这一环境被称为执行上下文。根据运行环境的不同,执行上下文主要划分为三类:

全局执行环境:这是代码首次执行时默认所处的环境。

函数执行环境:当程序执行流程进入某个函数体内部时,便进入该函数的执行环境。

Eval执行环境:当执行eval函数内部的文本代码时,会形成Eval执行环境。

二、执行上下文栈是什么

既然称为‘栈’,它必须遵循栈数据结构的核心特性,即先进后出(LIFO)。以下通过一段代码示例进行说明:


function cat(a){
    if(a<0){
        return false;
    }
    console.log('入栈:'+a);
    cat(a-1);
    console.log('出栈:'+a);
}
cat(3);
// 入栈:3
// 入栈:2
// 入栈:1
// 入栈:0
// 出栈:0
// 出栈:1
// 出栈:2
// 出栈:3

下面我们来分析上述代码的具体执行过程:

①浏览器加载页面时,程序首先进入全局执行上下文,并将其压入调用栈(作为第一个进入的元素,位于栈底);此时该执行上下文中包含一个函数定义cat、一次函数调用cat以及参数3。

②程序进入cat函数内部,即进入该函数的执行上下文,同样将其压入栈内(第二个进入的元素,位于倒数第二层)。由于传入参数3大于0,程序继续向下执行,输出'入栈:3'。

③程序继续执行,再次调用函数cat,此时进入一个新的函数执行上下文,并继续压入栈内(第三个进入的元素,位于倒数第三层),参数变为a-1。随后循环执行步骤②。需要注意的是,由于调用了cat(a-1),导致下一行代码'出栈:a'(此时a仍为3)的执行被挂起,即'出栈:3'暂时搁置在栈的倒数第二层。

④不断重复步骤②和③,依次输出'入栈:2'、'入栈:1'、'入栈:0';同时,被搁置的输出操作依次为'出栈:2'(位于栈内倒数第三层)、'出栈:1'(位于栈内倒数第四层)、'出栈:0'(位于栈内倒数第五层)。

⑤根据栈的先进后出特性,被搁置的4个输出操作依次执行,最终输出'出栈:0'、'出栈:1'、'出栈:2'、'出栈:3'。 以上即为执行上下文栈的运行情况,建议读者手动编写代码进行练习,以便更透彻地理解。

一、执行上下文是什么

三、执行上下文栈的过程细节

我们已知每次调用函数都会创建一个新的执行上下文,而每个执行上下文均包含两个阶段:创建阶段和执行阶段。 创建阶段:指程序调用函数但代码尚未开始执行的时期; 执行阶段:指变量分配、函数代码实际执行等阶段。

(一)创建阶段

在该阶段,当调用函数时,会创建一个执行上下文对象(executionContextObj)。该对象包含三个子对象:作用域链对象(scopeChain)、变量对象(variableObject,简称VO)以及this对象。其中,VO包含变量声明(variable)、函数声明(function)和参数(arguments)等。 这三个子对象分别在以下三个步骤中创建: 步骤1:初始化作用域链(scopeChain),并开辟栈内存。

步骤2:创建参数、函数和变量。

步骤3:确定上下文中this的值。

结合具体代码进一步分析上述步骤:


function cat(name) {
    var a = '年年';
    var b = function () {};
    this.name = name;
    function c() {
        console.log(this.name);
    }
    c();
}
cat('有鱼');

当调用函数cat('有鱼')时,执行上下文处于创建阶段,代码解析如下:


cat执行上下文对象 = {
    scopeChain: { ... },// 1.创建作用域链,开辟栈内存
    variableObject: { 2.创建变量对象
        arguments: {  // 2.1 解析参数
            0: '有鱼',
            length: 1
        },
        name: '有鱼',  // 2.1 解析参数,创建形参名称,并进行参数赋值
        c: function c()  // 2.2 找到函数声明c,并将c作为属性,function c作为值
        a: undefined,  // 2.3 找到变量声明a,初始化为undefined,该阶段只看声明部分,不进行赋值
        b: undefined   // 2.3 找到变量声明b,初始化为undefined,该阶段只看声明部分,不进行赋值
    },
    this: {  3.创建this对象
        this:cat('有鱼') // 3.1 指向此次调用的对象
        name:undefined // 3.2 对象属性name的初始化为undefined
    };
    c() //又进入函数c执行上下文,跟cat函数一样,暂不展开 
}

通过代码解析,我们可以得出以下结论:

①上述三个步骤的执行顺序不可颠倒。 ②在VO步骤中,先处理函数声明,再处理变量声明。 ③只有参数可以在创建阶段进行赋值,而变量和函数仅进行声明。

一、执行上下文是什么

(二)执行阶段

在此阶段,JavaScript解释器执行上下文中的函数代码,逐行运行JavaScript代码,并为变量赋值; 结合代码分析如下:


cat执行上下文对象 = {
    scopeChain: { ... },
    variableObject: { 
        arguments: { 
            0: '有鱼',
            length: 1
        },
        name: '有鱼', 
        c: function c(),
        a: '年年',  // 变量a进行赋值
        b: function b   // 变量b进行赋值
    },
    this: {  3.创建this对象
        this:cat('有鱼') 
        name:'有鱼' // 对象属性name进行赋值
    }
}

以上就是对JavaScript中执行上下文及调用栈的详细解析,更多关于JavaScript执行上下文及调用栈的资料,请关注本站其他相关文章!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章