详解JavaScript执行上下文与调用栈机制
本文深入解析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执行上下文及调用栈的资料,请关注本站其他相关文章!
JavaScript闭包与类(Class)的区别及适用场景对比
2026-11-05 15:48:57
JavaScript入门要学ES6吗?let与箭头函数等新特性解析
2026-11-05 14:49:16
JavaScript 8种数据类型详解:基本类型与引用类型的区别
2026-11-05 14:25:15
怎么在JavaScript中使用async/await优化Ajax异步流程
2026-11-05 13:56:15
JavaScript replace回调函数实现动态正则替换逻辑
2026-11-05 13:52:13
