Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
本题难度:⭐ ⭐
答:
执行栈,也叫调用栈,具有 LIFO(后进先出)结构,用于存储在代码执行期间创建的所有执行上下文。
当Javascript
引擎开始执行代码的时候,它就会创建一个全局执行上下文然后将它压到执行栈中。
每当引擎碰到一个函数的时候,它就会创建一个函数执行上下文,然后将这个执行上下文压到执行栈中。
引擎会执行位于执行栈栈顶的执行上下文(一般是函数执行上下文),当该函数执行结束后,对应的执行上下文就会被弹出,然后控制流程到达执行栈的下一个执行上下文。
示例
JS中,同步代码的执行可以参考下面这个示例:
const second = () => {
console.log('Hello there!')
}
const first = () => {
console.log('Hi there!')
second()
console.log('The End')
}
first()
当这段代码执行的时候,一个全局执行上下文(用main()来表示)就被创建出来并压入栈顶。后面调用first()的时候,又把first()压入栈顶。
之后,console.log('Hi there!')被压入栈顶,当它执行结束的时候,就从栈顶弹出。再之后,我们调用second(),second()函数被压入栈顶。
然后,console.log('Hello there!')被压入栈顶,当它执行结束的时候,就从栈顶弹出。之后,second()函数执行结束,从栈顶弹出。
然后console.log('The End')被压入栈顶,当它结束的时候,从栈顶弹出。再之后,first()函数执行结束,从栈顶弹出。
至此,这段代码就执行完毕了,同时全局执行上下文(main())从栈顶弹出。
栈溢出
调用栈是有大小的,当入栈的执行上下文超过一定数目,JavaScript 引擎就会报错,我们把这种错误叫做栈溢出。
比如执行这样一段代码,
function fn () {
console.log('test')
fn()
}
fn()
从上图你可以看到,抛出的错误信息为:超过了最大栈调用大小(Maximum call stack size exceeded)。
那为什么会出现这个问题呢?这是因为当 JavaScript 引擎开始执行这段代码时,它首先调用函数 fn,并创建执行上下文,压入栈中;
然而,这个函数是递归的,并且没有任何终止条件,所以它会一直创建新的函数执行上下文,并反复将其压入栈中,但栈是有容量限制的,超过最大数量后就会出现栈溢出的错误。
扩展:利用浏览器查看调用栈信息
执行一段复杂的代码时,你可能很难从代码文件中分析其调用关系,这时候你可以在你想要查看的函数中加入断点,然后当执行到该函数时,就可以查看该函数的调用栈了。
从图中可以看出,右边的 call stack 下面显示出来了函数的调用关系:
- 栈的最底部是 anonymous,也就是全局的函数入口;
- 中间是 first 函数;
- 顶部是 second 函数。 这就清晰地反映了函数的调用关系,所以在分析复杂结构代码,或者检查 Bug 时,调用栈都是非常有用的。
也可以使用 console.trace()
来输出当前的函数调用关系,如下图:
结尾
如果我的文章对你有帮助,你的👍就是对我的最大支持^_^
你也可以关注《前端每日一问》这个专栏,防止失联哦~
我是阿林,输出洞见技术,再会!
上一篇:
下一篇: