调用栈
控制流经过函数的方式有点复杂。 让我们仔细看看它。 这是一个简单的程序,它执行了一些函数调用:
function greet(who) {
console.log("Hello " + who);
}
greet("Harry");
console.log("Bye");
这个程序的执行大致是这样的:对greet
的调用使控制流跳转到该函数的开始(第 2 行)。 该函数调用控制台的console.log
来完成它的工作,然后将控制流返回到第 2 行。 它到达greet
函数的末尾,所以它返回到调用它的地方,这是第 4 行。 之后的一行再次调用console.log
。 之后,程序结束。
我们可以使用下图表示出控制流:
not in function
in greet
in console.log
in greet
not in function
in console.log
not in function
由于函数在返回时必须跳回调用它的地方,因此计算机必须记住调用发生处上下文。 在一种情况下,console.log
完成后必须返回greet
函数。 在另一种情况下,它返回到程序的结尾。
计算机存储此上下文的地方是调用栈。 每次调用函数时,当前上下文都存储在此栈的顶部。 当函数返回时,它会从栈中删除顶部上下文,并使用该上下文继续执行。
存储这个栈需要计算机内存中的空间。 当栈变得太大时,计算机将失败,并显示“栈空间不足”或“递归太多”等消息。 下面的代码通过向计算机提出一个非常困难的问题来说明这一点,这个问题会导致两个函数之间的无限的来回调用。 相反,如果计算机有无限的栈,它将会是无限的。 事实上,我们将耗尽空间,或者“把栈顶破”。
function chicken() {
return egg();
}
function egg() {
return chicken();
}
console.log(chicken() + " came first.");
// → ??