js中无限递归一定会导致栈溢出吗?

关于栈溢出,我们通常会感觉无限递归会导致栈溢出。但是一定如此吗?


什么是栈溢出

栈是一块 内存空间 ,而每一次函数调用,就相当于往内存空间内加入一个执行上下文。当这个函数没有执行完时,又调用了另一个函数(比方说自身),这个时候就又会往内存空间内加入一个执行上下文。反复如此的话,栈会被撑满,空间不够了,这就叫栈溢出。

例如下面这段代码就是个典型的无限递归场景。
在foo()调用结束之前又调用了foo(),因此会导致栈溢出。

1
2
3
4
function foo(){
foo()
}
foo()

什么情况下无限递归不会导致栈溢出

例如下面这种情况,就不会导致栈溢出

1
2
3
4
function foo(){
setTimeout(foo,0)
}
foo()

在调用foo()时,往执行栈内加入一个上下文。而在该上下文执行期间,会开启一个定时器,而计时器到达之后,会再一次执行foo函数。但由于JS语言是异步的,也就是说,foo函数本身不会等待计时器结束。
foo()在创建完计时器后,会运行结束,随后出栈。随后计时器到达,加入事件队列,而后执行上下文,调用foo(),重复该步骤,因此栈不会溢出。

变种

若是这种情况,则会导致栈溢出

1
2
3
4
function foo(){
setTimeout(foo(),0)
}
foo()

这是因为在开启定时器前,需要将foo()作为参数传入,所以会先执行foo()将其返回结果作为参数传入setTimeout。故变成了在计时之前调用foo(),因此会导致栈溢出。