调用栈

184 阅读3分钟

《用得上的前端知识》系列 - 你我都很忙,能用100字说清楚,绝不写万字长文

基本概念

调用栈:用来管理函数调用关系的一种后进先出的数据结构;

如何查看调用栈的信息

  • 方法一:
    • 打开开发者工具;
    • 点击“Source”标签,在左边选择(点击)想查看的 JavaScript 代码;
    • 点击中间代码区左侧的行号打上断点,然后刷新;
    • 点击控制台右边的“Call Stack”来查看当前的调用栈的情况。
  • 方法二:
    • 使用 console.trace() 来输出当前的函数调用关系。

调用栈的溢出条件

调用栈有两个指标,最大栈容量和最大调用深度,满足其中任意一个就会栈溢出。

调用栈的工作过程

  • JS 引擎根据代码类型创建执行上下文,并得到对应的可执行代码
  • 把创建的执行上下文压入栈底;
  • 控制器进入位于栈顶的执行上下文,开始执行与之对应的可执行代码
  • 可执行代码执行完毕,与之对应的执行上下文出栈;
  • 进入调用栈中下一个执行上下文,开始执行与之对应的代码,如此反复,直至剩下全局执行上下文

注意:只有当整个应用程序结束的时候,调用栈才会被清空,所以程序结束之前, 调用栈最底部永远有个全局执行上下文

能创建执行上下文的代码类型:

  • 全局代码,当 JavaScript 执行全局代码的时候,会编译全局代码并创建全局执行上下文,而且在整个页面的生存周期内,全局执行上下文只有一份;
  • 函数代码,当调用一个函数时,函数体内的代码会被编译,并创建函数执行上下文,一般情况下,函数执行结束之后,创建的函数执行上下文会被销毁;
  • eval 函数代码,当使用 eval 函数的时候,eval 的代码也会被编译,并创建执行上下文。

举例说明

var a = 2;

function add(b,c){  
    return b+c;
}

function addAll(b,c){
    var d = 10,
        result = add(b,c);
    return  a+result+d;
}

addAll(3,6)

上述代码在执行过程中,调用栈的变化情况如下:

1、创建全局上下文,并将其压入栈底,如下图所示:

2、JavaScript 引擎开始执行全局代码。首先会执行 a=2 的赋值操作,全局上下文变量环境中 a 的值设置为 2;

3、调用 addAll 函数。当调用该函数时,JavaScript 引擎会编译该函数,并为其创建一个执行上下文,最后还将该函数的执行上下文压入栈中;

4、进入了函数代码的执行阶段,这里先执行的是 d=10 的赋值操作,addAll 函数执行上下文中的 d 由 undefined 变成了 10;

5、当执行到 add 函数调用语句时,同样会为其创建执行上下文,并将其压入调用栈;

6、当 add 函数返回时,该函数的执行上下文就会从栈顶弹出,并将 result 的值设置为 add 函数的返回值,也就是 9;

7、紧接着 addAll 执行最后一个相加操作后并返回,addAll 的执行上下文也会从栈顶部弹出,此时调用栈中就只剩下全局上下文了。

参考资料