A terceira parte da série de noções básicas de JavaScript: Qual o papel da pilha de chamadas no mecanismo JavaScript?

No mês passado, escrevi um artigo sobre como o V8 executa o código JavaScript (deixe a = 1)? , Depois de escrevê-lo, descobri que sei tão pouco sobre o conhecimento de baixo nível da ferramenta V8 comumente usada. Ao mesmo tempo, meu tempo real no front-end é relativamente curto, então a base também é muito fraca. Combinando o acima, pretendo aprender e entender a partir da perspectiva de nível inferior quando tiver tempo, para entender e resolver os problemas encontrados durante o processo de uso, entender a essência do JavaScript e poder aprender melhor o JavaScript. Se você tem a mesma confusão que eu, então podemos ir juntos e aprender juntos.

Nesta série, continuarei a resumir da minha perspectiva:

prefácio

Através deste artigo você pode aprender o seguinte:

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

1. Estouro de memória

Vamos primeiro olhar para um código de chamada recursiva simples.

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

  recursion(1)
</script>

O resultado da execução é o seguinte

imagem.png

Ou seja, meu computador de teste recorreu cerca de 11.421 vezes e, em seguida, a pilha estourou. O número de 11.421 pode variar dependendo da configuração do computador. Tamanho máximo da pilha de chamadas excedido O tamanho máximo da pilha de chamadas excedido.

O problema já existe, por que está dando erro? Com esta pergunta em mente, continuamos a olhar para baixo.

2. Duas maneiras de visualizar a pilha de chamadas

Vamos ver outro pedaço 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 tela após executar o código

WeChat screenshot_20220620153034.png

Na primeira maneira, você pode quebrar o ponto na linha 5 (a posição da linha do código na captura de tela) através da captura de tela e visualizar as informações atuais da pilha de chamadas no lado direito.

A segunda maneira é através do console.trace(), o código acima adicionou o log de impressão, que pode ser visualizado diretamente

  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

Ao imprimir o log, podemos descobrir com mais clareza que quando a função atual for executada, ela será automaticamente removida do log de impressão.

Além disso, você pode ajustar a ordem de console.trace() para ver qual é a ordem dos pushes para a pilha de chamadas. Aqui está um breve resumo da minha experiência atual:

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

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

  • 以此类推......

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

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

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

3、调用栈的定义

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

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

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

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

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

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

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

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

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

A pilha de chamadas é uma estrutura de dados que mantém o controle de onde estamos no programa. Se executarmos uma função, o mecanismo JavaScript realmente a criará para a função atual 函数执行上下文neste 函数执行上下文momento e a colocará no topo da pilha. Ao retornar dessa função, ele a exibirá 函数执行上下文do topo da pilha Isso é o que a pilha de chamadas faz. Ou 执行上下文seja , ele é 调用栈gerenciado.

Resumir

  • 调用栈O espaço de armazenamento é limitado. Se você continuar inserindo-o na pilha e inserindo-o na pilha, ocorrerá um erro de estouro de memória.

  • 执行上下文é armazenado 调用栈em

  • 调用栈Compreender o funcionamento do JavaScript a partir da perspectiva de执行上下文

Estou participando do recrutamento do programa de assinatura de criadores da Comunidade de Tecnologia Nuggets, clique no link para se cadastrar e enviar .

Acho que você gosta

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