调用堆栈(一)-执行上下文和执行栈
执行上下文概念及类型
执行上下文可以理解为javascript代码被执行时的环境。
在js中,执行上下文分为以下三种类型:
- 全局执行上下文:只有一个,浏览器中的全局对象就是window对象,this指向这个全局对象。
- 函数执行上下文:存在无数个,只有在函数被调用的时候才会被创建,每次调用函数都会创建一个新的执行上下文。
- Eval函数执行上下文:指的是运行在eval函数中的代码,很少用而且不建议使用。
执行栈概念
执行栈,也叫调用栈,具有LIFO(后进先出)的结构,用于存储代码执行期间创建的所有执行上下文。
根据执行上下文的类型可以知道,在js程序执行过程中,一定会出现多种不同的执行上下文,但是,js是单线程语言,不能同时做多件事情。当js解释器初始执行代码时,它会首先默认进入全局上下文,而且每次调用一个函数都将会创建一个新的执行上下文,每次新创建的执行上下文都会被添加到作用域链的顶部,也就是执行栈或者调用栈。浏览器总是运行于调用栈的顶部的当前执行上下文。一旦完成,当前执行上下文就会从栈顶移除。
可以看下面的一个例子:
var a = 1; // 1、全局上下文环境
function bar (x) {
console.log('bar')
var b = 2;
fn(x + b); // 3、fn上下文环境
}
function fn(c) {
console.log(c)
}
bar(3); // 2、bar上下文环境
如下图:

执行上下文的创建
我们现在已经知道,每当调用一个函数时,一个新的执行上下文就会被创建出来。然而,在JavaScript引擎内部,这个执行上下文的创建过程具体分为两个阶段 :1、创建阶段,2、执行阶段
创建阶段(发生在当调用一个函数时,但是在执行函数体内的具体代码以前)
---建立变量,函数,arguments对象,参数
---建立作用链
---确定this的值
代码执行阶段:
---变量赋值,函数引用,执行其他代码
创建阶段
建立variableObject对象;
建立arguments对象,检查当前上下文中的参数,建立该对象下的属性以及属性值。
检查当前上下文中的函数声明:每找到一个函数声明,就在variableObject下面用函数名建立一个属性,属性值就是指向该函数在内存中的地址的一个引用,如果上述函数名已经存在与variableObject下,那么对应的属性值就会被新的引用所覆盖。
检查当前上下文中的变量声明:每找到一个变量的声明,就在variableObject下,用变量名建立一个属性,属性值为undefined。如果该变量名已经存在于variableObject属性中,直接跳过(防止指向函数的属性的值被变量属性覆盖为undefined),原属性值不会被修改。
初始化作用域链;
确定上下文中this的指向对象。
执行阶段
执行函数体中的代码,一行一行地运行代码,给variableObject中的变量属性赋值。
看个例子:
function f1(){
var n = 999;
function f2(){
alert(n);
}
return f2;
}
var result = f1();
result();//999

过程如下:

1、初始时全局上下文先入栈,遇到函数声明f1,用该函数名建立了一个属性指该函数f1,遇到变量声明result初始为undefined。
2、全局代码在执行过程中,遇到了f1()函数,执行var result=f1(),因此f1会创建对应的执行上下文并入栈。
3、在f1的可执行代码中,虽然声明了一个函数f2,但是并没有执行任何函数,因此也就不会产生别的执行上下文,代码执行结束后,f1自然会出栈
4、f1()函数执行后,开始执行result(),result函数会创建一个新的执行上下文,这个时候result的上下文入栈。
5、这个result()其实就是在f1中声明的函数f2,因此这个时候就会执行f2的代码,由于f2中没有产生新的执行上下文,因此执行完就会出栈。
参考:
https://muyiy.cn/blog/1/1.1.html#执行上下文的创建
https://github.com/LinDaiDai/niubility-coding-js/blob/master/JavaScript/调用堆栈/JavaScript进阶-执行上下文.md
https://m528964214.blog.csdn.net/article/details/87898267
https://www.cnblogs.com/mcray/p/7003245.html?utm_source=itdadao&utm_medium=referral

浙公网安备 33010602011771号