调用栈

调用栈是解释器(比如 Web 浏览器中的 JavaScript 解释器)用来追踪其在调用多个函数的脚本中所处位置的机制——当前正在运行的函数是哪一个,以及在该函数内部调用了哪些函数,等等。

  • 当脚本调用一个函数时,解释器就会把该函数添加进调用栈,然后开始执行这个函数。
  • 正在调用栈中执行的函数调用的其他任何函数也将会被添加进调用栈。一旦这个函数被调用,便会立即执行。
  • 当前函数执行完毕后,解释器将其清出调用栈,继续执行当前执行环境下的剩余的代码。
  • 当分配的调用栈空间被占满时,会引发“堆栈溢出”错误。
js
function greeting() {
  // [1] 这里有一些代码
  sayHi();
  // [2] 这里有一些代码
}
function sayHi() {
  return "Hi!";
}

// 调用 `greeting` 函数
greeting();

// [3] 这里有一些代码

上面的代码会按照如下流程这样执行:

  1. 忽略前面所有函数,直到 greeting() 函数被调用。

  2. greeting() 添加进调用栈列表,然后我们就得到了:

    - greeting
    
  3. 执行 greeting() 函数体中的所有代码。

  4. 代码执行到 sayHi() 的调用语句。

  5. sayHi() 添加进调用栈列表,就像:

    - sayHi
    - greeting
    
  6. 执行 sayHi() 函数体中的代码,直到全部执行完毕。

  7. 返回来继续执行 greeting() 函数体中 sayHi() 后面的代码。

  8. 删除调用栈列表中的 sayHi() 函数。现在,调用栈看起来像:

    - greeting
    
  9. greeting() 函数体中的代码全部执行完毕,返回到调用它的代码行,继续执行剩下的 JS 代码。

  10. 删除调用栈列表中的 greeting() 函数。调用栈再次变空。

一开始,我们得到一个空白的调用栈。随后,每当有函数被调用都会自动地添加进调用栈,执行完函数体中的代码后,调用栈又会自动地移除这个函数。最后,我们又得到了一个空白的调用栈。

参见