现在我们来仔细了解一下函数的控制流程,这有助于我们加深对JavaScript语言本身的理解。以下是一个简单的程序,进行了几次函数调用:
该程序大致的执行流程是:调用greet函数,此时控制流跳转到函数的开头(第2行)。然后,调用console.log(浏览器的内置函数),此时控制流将会跳转到该函数并完成相关操作,执行结束后控制流返回到第2行。接着控制流到达greet函数的结尾,返回调用该函数的位置,即第4行。最后在函数调用后再次调用console.log。
我们可以使用下图表示出控制流:
由于函数需要在执行结束后跳转回调用该函数的代码位置,因此计算机必须记住函数调用的上下文。比如说,代码中的第一个console.log调用需要在执行结束后返回greet函数。而第二个console.log调用则返回到程序结尾的位置。
我们将计算机存储这个上下文的区域称之为调用栈。每当函数调用时,当前的上下文信息就会被存储在栈顶。而当函数返回时,系统会删除存储在栈顶的上下文信息,并使用该信息继续执行程序。
栈需要保存在计算机内存中。若栈存储的空间过大,计算机就会提示类似于“栈空间溢出”或“递归过多”的信息。以下代码展示了这种情况的发生,该程序会向计算机提出一个难题,进而导致两个函数之间来回调用,并无限循环下去。当然了,如果计算机的栈空间无限大,那么这个循环调用确实会永远执行下去。实际情况是该程序会耗尽内存空间,导致“栈空间溢出”。