ボンネットの下の JavaScript

Nov 28 2022
目次 この記事では、JavaScript の内部動作と実際の実行方法について詳しく説明します。詳細を理解することで、コードの動作を理解できるようになり、より優れたアプリを作成できるようになります。

目次

  • スレッドとコール スタック
  • 実行コンテキスト
  • イベントループと非同期 JavaScript
  • メモリ ストレージとガベージ コレクション
  • JIT (ジャストインタイム) コンパイル
  • 概要

この記事では、JavaScript の内部動作と実際の実行方法について詳しく説明します。詳細を理解することで、コードの動作を理解できるようになり、より優れたアプリを作成できるようになります。

JavaScript は次のように記述されます。

シングルスレッド、ガベージ コレクション、インタープリター、またはジャスト イン タイムでコンパイルされた、ノンブロッキング イベント ループを備えたプログラミング言語。

これらの重要な用語をそれぞれ説明しましょう。

スレッドとコール スタック:

JavaScript エンジンは、プログラムの実行に使用されるヒープと単一の呼び出しスタックで構成されるシングルスレッド インタープリターです。コール スタック
は、後入れ先出し (LIFO) の原則を使用して、関数の呼び出し (呼び出し) を一時的に格納および管理するデータ構造です。これは、スタックに最後にプッシュされた関数が、関数が戻ったときに最初にポップアウトされることを意味します。呼び出しスタックは単一であるため、関数の実行は一度に 1 つずつ、上から下に行われます。これは、コール スタックが同期していることを意味します。

さて、これは同期なので、JavaScript は非同期呼び出しをどのように処理できるのでしょうか?
イベント ループは、JavaScript の非同期プログラミングの背後にある秘密です。
しかし、JavaScript 内での非同期呼び出しの概念と、シングルスレッド言語でそれがどのように可能であるかを説明する前に、まずコードがどのように実行されるかを理解しましょう。

実行コンテキスト (EC):

実行コンテキストは、JavaScript コードが実行される環境として定義されます。
実行コンテキストの作成は、次の 2 つのフェーズで行われます。

1. メモリ作成フェーズ:

  • グローバル オブジェクト (ブラウザーではウィンドウ オブジェクト、NodeJS ではグローバル オブジェクトと呼ばれます) を作成します。
  • 「this」オブジェクトを作成し、それをグローバル オブジェクトにバインドします。
  • 変数と関数の参照を格納するためのメモリ ヒープ (ヒープとは、大規模でほとんど構造化されていないメモリ領域) の設定。
  • Hoistingを実装することにより、関数と変数をグローバル実行コンテキストに格納します。

コード実行の背後にあるステップがわかったので、元に戻りましょう。

イベント ループ:

まず、この図を見てみましょう。

JS でのイベント ループ

2 つの主要コンポーネントで構成されるエンジンがあります。
* メモリ ヒープ — ここでメモリ割り当てが行われます。
* コール スタック — これは、コードの実行時にスタック フレームが存在する場所です。

アクセスできないスレッドであるWeb APIがあり、それらを呼び出すだけです。それらは、DOM、AJAX、setTimeout などのように、並行性が有効になるブラウザーの一部です。

最後に、処理するイベントのリストであるコールバック キューがあります。各イベントには、それを処理するために呼び出される関連関数があります。

では、ここでのイベント ループのタスクは何でしょうか。
イベント ループには、コール スタックとコールバック キューを監視するという 1 つの単純なジョブがあります。コール スタックが空の場合、イベント ループはキューから最初のイベントを取得し、それをコール スタックにプッシュして効果的に実行します。
このような反復は、イベント ループではティックと呼ばれます。各イベントは単なる関数コールバックです。

メモリ ストレージとガベージ コレクション:

ガベージ コレクションの必要性を理解するには、まずメモリ ライフ サイクルを理解する必要があります。これはどのプログラミング言語でもほぼ同じで、3 つの主要なステップがあります。
1. メモリを割り当てます。
2. 割り当てられたメモリを読み取り、書き込み、またはその両方に使用します。
3. 不要になったら、割り当てられたメモリを解放します。

メモリ管理の問題の大部分は、割り当てられたメモリを解放しようとしたときに発生します。発生する主な懸念事項は、未使用のメモリ リソースの決定です。
メモリが不要になる時期を開発者が手動で決定する必要がある低レベル言語の場合、JavaScript などの高レベル言語では、ガベージ コレクション (GC) と呼ばれる自動化された形式のメモリ管理が使用されます。
JavaScript は、GC を実行するために 2 つの有名な戦略を使用します。参照カウント手法とマーク アンド スイープ アルゴリズムです。両方のアルゴリズムとその仕組みに関するMDN
の詳細な説明を次に示します。

JIT (ジャストインタイム) コンパイル:

JavaScript の定義に戻りましょう。「解釈され、JIT コンパイルされたプログラミング言語」と書かれていますが、それはどういう意味ですか? 一般的なコンパイラとインタプリタの違いから始めてみませんか?

類推として、コミュニケーションを取りたい言語の異なる 2 人の人物について考えてみましょう。コンパイルは、言語を学ぶために立ち止まって時間を費やすようなものであり、通訳は、各文を解釈するために誰かがそこにいるようなものです.

したがって、コンパイル済み言語は書き込み時間が遅く、実行時間が速く、インタープリター型言語はその逆です。

専門用語で言えば、コンパイルとは、プログラムのソース コードを実行前に機械で読み取り可能なバイナリ コードに変換するプロセスであり、コンパイラはプログラム全体を一度に処理します。

一方、インタープリターは、プログラム命令を機械可読形式にプリコンパイルする必要なく実行するプログラムであり、一度に 1 行のコードを使用します。

そして、解釈されたプログラムのパフォーマンスを向上させる JIT コンパイルの役割がここにあります。コード全体が一度に機械語に変換され、すぐに実行されます。

JIT コンパイラーの内部には、モニター (別名プロファイラー) と呼ばれる新しいコンポーネントがあります。そのモニターは、実行中のコードを監視し、

  • コードのホットまたはウォーム コンポーネントを特定します。例: 反復コード。
  • 実行時にこれらのコンポーネントをマシン コードに変換します。
  • 生成されたマシン コードを最適化します。
  • コードの以前の実装をホットスワップします。

核となる概念を理解したところで、すべてをまとめて、コードの実行中にJS Engineがたどるステップを要約してみましょう。

画像ソース: traversy メディア チャンネル
  1. JS エンジンは、人間が読める構文で記述された JS コードを受け取り、それをマシン コードに変換します。
  2. エンジンはパーサーを使用してコードを 1 行ずつ調べ、構文が正しいかどうかを確認します。エラーが発生すると、コードの実行が停止し、エラーがスローされます。
  3. すべてのチェックに合格すると、パーサーは抽象構文ツリー (AST) と呼ばれるツリー データ構造を作成します。
  4. AST は、ツリー状の構造でコードを表すデータ構造です。コードを AST からマシン コードに変換する方が簡単です。
  5. 次に、インタープリターは AST を受け取り、それを IR に変換します。IR はマシン コードの抽象化であり、JS コードとマシン コードの間の仲介役です。IR は最適化を実行することもでき、よりモバイルです。
  6. 次に、JIT コンパイラーは、生成された IR を受け取り、コードをコンパイルしてその場でフィードバックを取得し、そのフィードバックを使用してコンパイル プロセスを改善することにより、それをマシン コードに変換します。

読んでくれてありがとう :)

TwitterとLinkedInで私をフォローしてください。