JavaScriptの基本シリーズの第3部:JavaScriptエンジンでコールスタックはどのような役割を果たしますか?

先月、V8がJavaScript(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の数は、コンピュータの構成によって異なる場合があります。最大コールスタックサイズを超えました最大コールスタックサイズを超えました。

問題はすでにありますが、なぜエラーが発生するのですか?この質問を念頭に置いて、私たちは見下し続けます。

2.コールスタックを表示する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>

コードを実行した後のスクリーンショット

WeChatscreenshot_20220620153034.png

最初の方法では、5行目(スクリーンショットのコードの行位置)のポイントをスクリーンショットで区切ることができ、右側に現在のコールスタック情報を表示できます。

2番目の方法は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()の順序を調整して、呼び出しスタックへのプッシュの順序を確認することもできます。これが私の現在の経験の簡単な要約です:

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

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

  • 以此类推......

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

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

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

3、调用栈的定义

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

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

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

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

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

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

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

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

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

コールスタックは、プログラムのどこにいるかを追跡するデータ構造です。関数を実行すると、JavaScriptエンジンが実際にこの時点で現在の関数用に作成し函数执行上下文函数执行上下文スタックの一番上に配置します。この関数から戻ると、スタックの一番上からこれを函数执行上下文ポップします。 。これは、コールスタックが行うことです。つまり、それ执行上下文调用栈によって管理されます。

要約する

  • 调用栈ストレージスペースに限りがあります。スタックにプッシュし続けてスタックにプッシュすると、メモリオーバーフローエラーが発生します。

  • 执行上下文に保存さ调用栈れます

  • 调用栈JavaScriptの操作を次の観点から理解する执行上下文

ナゲッツテクノロジーコミュニティのクリエイター署名プログラムの募集に参加しています。リンクをクリックして登録し、送信してください。

おすすめ

転載: juejin.im/post/7117839908055547917