文章目录
1.下面三段代码会执行结果什么不同
function foo() {
foo() // 是否存在堆栈溢出错误?
}
foo()
function foo() {
setTimeout(foo, 0) // 是否存在堆栈溢出错误?
}
function foo() {
return Promise.resolve().then(foo)
}
foo()
A:
- 第一段:V8就会报告 栈溢出的错误
- 第二段:正确执⾏
- 第三段:没有栈溢出的错误,却会造成⻚⾯的卡死
2.为什么第一段会栈溢出
由于foo函数内部嵌套调⽤它⾃⼰,所以在调⽤foo函数的时候,它的栈会⼀直向上增⻓,但是由于栈空间在
内存中是连续的,所以通常我们都会限制调⽤栈的⼤⼩,如果当函数嵌套层数过深时,过多的执⾏上下⽂堆
积在栈中便会导致栈溢出,最终如下图所⽰:
3.为什么第二段会正常
setTimeout的本质是将同步函数调⽤改成异步函数调⽤,
这⾥的异步调⽤是将foo封装成事件,并将其添加进 消息队列中,然后主线程再按照⼀定规则循环地从消息队列中读取下⼀个任务。
⾸先,主线程会从消息队列中取出需要执⾏的宏任务,假设当前取出的任务就是要执⾏的这段代码,这时候
主线程便会进⼊代码的执⾏状态。这时关于主线程、消息队列、调⽤栈的关系如下图所⽰
接下来V8就要执⾏foo函数了,同样执⾏foo函数时,会创建foo函数的执⾏上下⽂,并将其压⼊栈中,最终
效果如下图所⽰:
当V8执⾏执⾏foo函数中的setTimeout时,setTimeout会将foo函数封装成⼀个新的宏任务,并将其添加到
消息队列中,在V8执⾏setTimeout函数时的状态图如下所⽰:
等foo函数执⾏结束,V8就会结束当前的宏任务,调⽤栈也会被清空,调⽤栈被清空后状态如下图所⽰