JavaScript基础系列(3):调用栈在JavaScript引擎中扮演了一个什么样的角色

简介: 也就是说我测试电脑当时递归了11421次左右之后栈溢出了,11421这个数字根据电脑配置不同可能有一些出入。Maximum call stack size exceeded超出最大调用栈的大小了。

image.png


上个月写过一篇V8是如何运行JavaScript(let a = 1)代码的?,写完之后我就发现,我对平常使用的工具V8引擎,偏底层的知识了解的竟然是如此甚少。同时我真正从事前端的时间还算是比较短的,那么基础也算是非常的薄弱。结合以上,我打算有时间就去从底层的角度去学习了解,便于在使用过程中的理解和解决遇到的问题,理解JavaScript的本质,能够更好的学习JavaScript。如果你跟我有同样的困惑,那我们可以结伴同行,共同学习。


本系列我会从我的视角不断的去总结:





前言


通过本篇你可以学习到以下内容:


1、为什么会存在栈溢出?
2、调用栈的定义
3、执行上下文的管理方式
4、调用栈的作用


1、内存溢出


先来看一段简单的递归调用代码吧


<script>
  function recursion(x) {
    console.log(x)
    recursion(x)
  }
  recursion(1)
</script>


执行的结果如下


image.png


也就是说我测试电脑当时递归了11421次左右之后栈溢出了,11421这个数字根据电脑配置不同可能有一些出入。Maximum call stack size exceeded超出最大调用栈的大小了。


问题已经有了,为什么会报错呢?带着这个疑问我们继续往下看。


2、查看调用栈的两种方式


我们再来看一段代码


<script>
  var a = 10
  function add_d() {
    var d = 40
    console.trace('add_d正在执行')
    return a + d
  }
  function add_c() {
    var c = 30
    var dd = add_d()
    console.trace('add_d已经执行结束,从call stack弹出')
    return c + dd
  }
  function add_b() {
    var b = 20
    let cc = add_c()
    console.trace('add_c已经执行结束,从call stack弹出')
    return b + cc
  }
  add_b()
  console.trace('add_b已经执行结束,从call stack弹出')
</script>


执行代码后的截图


image.png


第一种方式通过截图可以在第5行(截图中代码的行位置)的位置打断点,在右侧就可以查看到当前的调用堆栈信息。


第二种方式通过console.trace(),我上面的代码其实已经加入了打印日志,可以直接查看


console.trace
  add_d @ js执行过程.html:16
  add_c @ js执行过程.html:22
  add_b @ js执行过程.html:27
(匿名) @ js执行过程.html:30   // 这里的匿名相当于全局进行
js执行过程.html:16 add_d正在执行
add_d @ js执行过程.html:16
add_c @ js执行过程.html:22
add_b @ js执行过程.html:29
(匿名) @ js执行过程.html:34
js执行过程.html:23 add_d已经执行结束,从call stack弹出
add_c @ js执行过程.html:23
add_b @ js执行过程.html:29
(匿名) @ js执行过程.html:34
js执行过程.html:30 add_c已经执行结束,从call stack弹出
add_b @ js执行过程.html:30
(匿名) @ js执行过程.html:34
js执行过程.html:35 add_b已经执行结束,从call stack弹出
(匿名) @ js执行过程.html:35


通过打印日志,我们可以更清晰的发现,当当前函数执行完毕以后,它会自动的从打印日志中移除了。


同样的你可以调整console.trace()的顺序去查看到压入call stack的顺序是什么样的。这里根据我目前的经验简单总结如下:


  • 当JavaScript调用一个函数的时候,JavaScript引擎遍会为其创建执行上下文,并把该执行上下文压入调用栈,然后JavaScript引擎开始执行函数的代码。


  • 执行函数时如果又发现有函数被调用,则会继续将该函数的执行上下文压入调用栈,然后继续开始执行函数中的代码。


  • 以此类推......


  • 当某个函数执行完毕的时候,JavaScript引擎会将函数的执行上下文弹出栈。


  • 调用栈的空间满了以后,就会引发堆栈溢出的问题。


因为调用栈的空间是有限的,所以我们开篇里的小例子不断的递归,根本停不下来,迟早会发生栈溢出,也就是我上面截图的错误。


3、调用栈的定义


我们先来理解一下栈的数据结构,通过一个小故事来进行简单的理解:


自助餐厅有一堆餐盘,工作人员洗好之后,将一批餐盘一个一个的叠加到一起,由于各种原因(餐盘的摆放位置、以及方便客人拿取等等),每批餐盘都有一定的高度限制,肯定不能无限高。接下来我们就来分析其中一批被叠拼摆好的餐盘吧,先假设一下餐盘的高度是35(总共摆放了35个餐盘)


  • 每次将餐盘叠拼上去的时候就相当于入栈


  • 这个操作一直执行了35次,因为一直没有使用


  • 接着到中午开始排队吃饭的时候,到了取餐盘的地方,就有人开始从顶部取走一个餐盘(一般都是从顶部取,特殊情况这里我们就不讨论了)


  • 每次取餐盘的操作就相当于出栈


  • 这个操作一直执行了35次,因为排队吃饭就要使用餐盘,就要使用


  • 最终35个餐盘都被取走了


可以发现,先被叠拼的餐盘,要最后才被取出,而刚被叠拼的餐盘,第一个就被拿走了。遵循的原则便是:先进后出、后进先出的原则。


调用栈是一种数据结构,它记录了我们在程序中的位置。如果我们运行到一个函数,这个时候JavaScript引擎其实是会为当前函数创建函数执行上下文,它就将该函数执行上下文放到栈顶,当从这个函数返回的时候,就会将这个函数执行上下文从栈顶弹出,这就是调用栈做的事情。也就是说执行上下文是通过调用栈来管理的。


总结


  • 调用栈的存储空间是有限的,如果一直压入栈,压入栈,就会出现内存溢出的错误。


  • 执行上下文就是存储在调用栈中的


  • 调用栈的角度了解JavaScript的运行,结合执行上下文
目录
相关文章
|
19天前
|
XML 缓存 JavaScript
一篇文章讲明白JS模板引擎之JST模板
一篇文章讲明白JS模板引擎之JST模板
14 2
|
1月前
|
缓存 自然语言处理 前端开发
深入剖析JavaScript引擎的工作原理
【6月更文挑战第3天】JavaScript引擎由解析器、解释器、优化器和垃圾回收器组成,它们协同完成代码的解析、编译和执行。解析器将源代码转为抽象语法树(AST),编译阶段进行作用域分析和变量提升。解释器执行AST,优化器在代码频繁执行时进行即时编译以提高性能。垃圾回收器自动回收不再使用的内存,防止泄漏。理解这些原理有助于优化代码和提升Web应用性能。
18 1
|
2月前
|
JavaScript 前端开发 NoSQL
【MongoDB 专栏】MongoDB 的 JavaScript 引擎与脚本执行
【5月更文挑战第11天】MongoDB 的 JavaScript 引擎允许在服务器端直接执行脚本,提升效率并实现定制化操作。脚本环境提供独立但与数据库关联的运行空间,引擎负责脚本的解析、编译和执行。执行过程包括脚本提交、解析、编译和执行四个步骤。掌握脚本逻辑设计和 JavaScript 语言特性对于高效利用这一功能至关重要。例如,通过脚本可以计算商品总销售额,增强数据库操作的灵活性。
【MongoDB 专栏】MongoDB 的 JavaScript 引擎与脚本执行
|
2月前
|
JavaScript 前端开发 开发者
Vue.js深度解析:前端开发的生产力引擎
Vue.js深度解析:前端开发的生产力引擎
58 0
|
9月前
|
JavaScript 前端开发 算法
带你读《现代Javascript高级教程》十一、JavaScript引擎的垃圾回收机制(1)
带你读《现代Javascript高级教程》十一、JavaScript引擎的垃圾回收机制(1)
|
9月前
|
JavaScript 前端开发 算法
带你读《现代Javascript高级教程》十一、JavaScript引擎的垃圾回收机制(2)
带你读《现代Javascript高级教程》十一、JavaScript引擎的垃圾回收机制(2)
|
9月前
|
Web App开发 自然语言处理 JavaScript
带你读《现代Javascript高级教程》十、JavaScript引擎的工作原理:代码解析与执行(1)
带你读《现代Javascript高级教程》十、JavaScript引擎的工作原理:代码解析与执行(1)
|
9月前
|
Web App开发 缓存 JavaScript
带你读《现代Javascript高级教程》十、JavaScript引擎的工作原理:代码解析与执行(2)
带你读《现代Javascript高级教程》十、JavaScript引擎的工作原理:代码解析与执行(2)
|
11月前
|
JavaScript 前端开发
JS引擎的执行机制event loop
JS引擎的执行机制event loop
51 0
|
移动开发 JavaScript 前端开发