La tercera parte de la serie de conceptos básicos de JavaScript: ¿Qué papel juega la pila de llamadas en el motor de JavaScript?

El mes pasado escribí un artículo sobre cómo V8 ejecuta el código JavaScript (let a = 1). , Después de escribirlo, descubrí que sé muy poco sobre el conocimiento de bajo nivel del motor V8 de herramientas de uso común. Al mismo tiempo, mi tiempo real en front-end es relativamente corto, por lo que la base también es muy débil. Combinando lo anterior, planeo aprender y comprender desde la perspectiva de nivel inferior cuando tenga tiempo, para comprender y resolver los problemas encontrados durante el proceso de uso, comprender la esencia de JavaScript y poder aprender JavaScript mejor. Si tienes la misma confusión que yo, entonces podemos ir juntos y aprender juntos.

En esta serie, continuaré resumiendo desde mi perspectiva:

prefacio

A través de este artículo puedes aprender lo siguiente:

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

1. Desbordamiento de memoria

Primero veamos un código de llamada recursivo simple.

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

  recursion(1)
</script>

El resultado de la ejecución es el siguiente

imagen.png

Es decir, mi computadora de prueba recurrió aproximadamente 11421 veces y luego la pila se desbordó. El número de 11421 puede variar según la configuración de la computadora. Se excedió el tamaño máximo de la pila de llamadas Se excedió el tamaño máximo de la pila de llamadas.

El problema ya está ahí, ¿por qué está dando un error? Con esta pregunta en mente, seguimos mirando hacia abajo.

2. Dos formas de ver la pila de llamadas

Veamos otra pieza de código.

<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>

Captura de pantalla después de ejecutar el código

WeChat captura de pantalla_20220620153034.png

En la primera forma, puede dividir el punto en la línea 5 (la posición de la línea del código en la captura de pantalla) a través de la captura de pantalla y puede ver la información de la pila de llamadas actual en el lado derecho.

La segunda forma es a través de console.trace(), el código anterior en realidad ha agregado el registro de impresión, que se puede ver directamente

  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

Al imprimir el registro, podemos saber más claramente que cuando se ejecuta la función actual, se eliminará automáticamente del registro de impresión.

También puede ajustar el orden de console.trace() para ver cuál es el orden de las inserciones en la pila de llamadas. He aquí un breve resumen de mi experiencia actual:

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

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

  • 以此类推......

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

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

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

3、调用栈的定义

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

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

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

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

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

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

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

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

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

La pila de llamadas es una estructura de datos que realiza un seguimiento de dónde estamos en el programa. Si ejecutamos una función, el motor de JavaScript realmente la creará para la función actual 函数执行上下文en 函数执行上下文ese momento y la colocará en la parte superior de la pila. Al regresar de esta función, la mostrará 函数执行上下文desde la parte superior de la pila . Esto es lo que hace la pila de llamadas. Es decir, 执行上下文se 调用栈gestiona a través.

Resumir

  • 调用栈El espacio de almacenamiento es limitado. Si continúa empujándolo en la pila y lo empuja dentro de la pila, habrá un error de desbordamiento de memoria.

  • 执行上下文se almacena 调用栈en

  • 调用栈Comprender el funcionamiento de JavaScript desde la perspectiva de执行上下文

Estoy participando en el reclutamiento del programa de firma de creadores de la Comunidad Tecnológica de Nuggets, haga clic en el enlace para registrarse y enviar .

Supongo que te gusta

Origin juejin.im/post/7117839908055547917
Recomendado
Clasificación