调用栈

463 阅读3分钟

笔记参考了以下资料内容:
调用栈(MDN)
JavaScript 如何工作:对引擎、运行时、调用堆栈的概述
理解JavaScript中的执行上下文和执行栈

想要了解什么是调用栈之前,需要先了解执行上下文的相关概念,作为前置知识。

什么是执行上下文?

执行上下文是指评估和执行javascript代码的环境的抽象概念。每当JavaScript代码在运行的时候,它都是在执行上下文中运行。

执行上下文的类型

  • 全局执行上下文 这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的window对象,并且设置this的值等于这个全局对象。一个程序中只会有一个全局执行上下文

  • 函数执行上下文 每当一个函数被调用时,都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。

  • Eval函数执行上下文 * 执行在Eval函数内部的代码也会有属于它自己的执行上下文,但由于不常试用,不作讨论

了解完,执行上下文的相关概念后,我们就可以讨论关于调用栈的知识了。

什么是调用栈

调用栈(也被称为执行栈)是一种拥有后进先出特征的数据结构的栈,被用来存储代码运行时创建的所有执行上下文。
JavaScript引擎通过这种数据结构来管理函数执行的顺序,也是浏览器javascript解释器追踪函数执行流的一种机制,当执行环境调用了多个函数时,通过这种机制,我们能够追踪函数执行到哪一步。
因为JavaScript是一门单线程语言,所以浏览器中只会存在一个调用栈。

表现

  1. 当 JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行上下文并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压入栈的顶部。

  2. 引擎会执行那些执行上下文位于栈顶的函数。当该函数执行结束时,执行上下文从栈中弹出,控制流程到达当前栈中的下一个上下文。

  3. 当分配的调用栈空间被占满时,会引发"堆栈溢出"

示例

let a = 'Hello World!';

function first() {
  console.log('Inside first function');
  second();
  console.log('Again inside first function');
}

function second() {
  console.log('Inside second function');
}

first();
console.log('Inside Global Execution Context');

Untitled Diagram.png

当上述代码在浏览器加载时,JavaScript引擎创建了一个全局执行上下文并把它压入当前执行栈。当遇到 first() 函数调用时,JavaScript引擎为该函数创建一个新的执行上下文并把它压入当前执行栈的顶部。

当从 first() 函数内部调用 second() 函数时,JavaScript 引擎为second()函数创建了一个新的执行上下文并把它压入当前执行栈的顶部。

当 second() 函数执行完毕,它的执行上下文会从当前栈弹出,并且控制流程到达下一个执行上下文,即 first() 函数的执行上下文。

当 first() 执行完毕,它的执行上下文从栈弹出,控制流程到达全局执行上下文。一旦所有代码执行完毕,JavaScript 引擎从当前栈中移除全局执行上下文。