一、无限递归一定会溢出吗?
- 在浏览器控制台中运行
foo
函数,是否会导致堆栈溢出jsfunction foo() { setTimeout(foo, 0); // 是否存在堆栈溢出错误? } function foo() { setTimeout(foo(), 0); // 是否存在堆栈溢出错误? } function foo() { Promise.resolve().then(foo);// 是否存在堆栈溢出错误? }
题解:
- 从题中,知道在
setTimeout
中调用了foo
,自己函数内部调用自己,属于无限递归,那会不会导致堆栈溢出呢? - 首先要知道栈溢出的原因是什么,比如在一个函数里面无限的调用自身,就容易导致栈溢出,为什么呢?
- 因为每一次的函数调用,都会在栈里面加上一个执行上下文,其实就是会占用栈的一部分内存空间,而这个上下文呢,是要到函数结束了过后才会被弹出栈, 而如果说在这个函数里面,调用自身的话,就会导致这个函数还没有运行结束的时候,又在调用自己,又是一个函数调用,又会加一个进栈,一直加,一直调, 而栈的总大小是固定的,就会把栈撑爆,这就叫栈溢出。
- 那么这里会不会导致栈溢出呢?不会。是因为在
foo
函数内部调用自己是用异步
来操作的,setTimeout(foo, 0);
,当在执行栈中加上一个执行上下文 在执行foo
期间,并没有再次调用foo
,只是在执行期间做了一个计时器,告诉你计时器到达了之后,再去调用foo
,这就涉及到了事件循环,所以这一次的foo
是正常执行完成的,然后从事件队列里面取出这个foo
函数,进行下一次调用,而这次调用是一样的,开一个计时器去计时,弹出栈,计时完成,进行下一次 调用。所以说这种情况下,不会导致栈溢出的。 - 如果改成
setTimeout(foo(), 0);
,这个时候就会导致栈溢出了,这个时候就不是计时到了执行foo
,而是先执行foo
,把foo
的返回结果,作为计时器 到达之后的要执行的东西,所以会导致栈溢出。 - 还可以改成
Promise.resolve().then(foo);
,也是不会导致栈溢出。