フロントエンドの高度なJSの動作原理

JS の仕組み

V8 エンジンの原理について詳しくはこちら

ブラウザ カーネルは、Webkit を例として、2 つの部分で構成されています。

  • WebCore: HTML の解析、レイアウト、レンダリング、およびその他の関連作業を担当します。
  • JavaScriptCore: JavaScript コードを解析して実行します。

画像-20230224183949294

V8 エンジンの公式定義:

  • V8 は、C++ で書かれた Google のオープンソースの高性能 JavaScript および WebAssembly エンジンで、特に Chrome や Node.js で使用されています。
  • ECMAScript と WebAssembly を実装しており、
    x64、IA-32、ARM、または MIPS プロセッサを使用する Windows 7 以降、macOS 10.12 以降、および Linux システム上で実行されます。
  • V8 はスタンドアロンで実行することも、任意の C++ アプリケーションに埋め込んで実行することもできます。

画像-20230224184029597

V8 エンジンのアーキテクチャは非常に複雑なので、まずその巨大なエンジンのいくつかのモジュールを理解することができます。

  • インタプリタは JavaScript コードを直接理解しないため、Parse モジュールは JavaScript コードを AST (抽象構文ツリー) に変換します。
    • 関数が呼び出されない場合、AST に変換されません。
    • Parse の V8 公式ドキュメント: https://v8.dev/blog/scanner
  • IgnitionはASTをByteCode(バイトコード)に変換するインタープリタです
    • 同時に、TurboFanの最適化に必要な情報(関数パラメータの型情報など、その型のみで実際の操作が実行できる)が収集されます。
    • 関数が 1 回だけ呼び出された場合、Ignition は ByteCode を解釈して実行します。
    • Ignition の V8 公式ドキュメント: https://v8.dev/blog/ignition-interpreter
  • TurboFan は、バイトコードを CPU が直接実行できるマシンコードにコンパイルするコンパイラーです。
    • 関数が複数回呼び出された場合、その関数はホット関数としてマークされ、コードの実行パフォーマンスを向上させるために TurboFan によって最適化されたマシンコードに変換されます。
    • マシン コードは実際には ByteCode に復元されます。これは、その後の関数の実行中に型が変更された場合 (たとえば、sum 関数は最初は数値型で
      実行されマシンコードは操作を正しく処理できないため、バイトコードに逆変換されます。
    • TurboFan の V8 公式ドキュメント: https://v8.dev/blog/turbofan-jit

V8 アーキテクチャ分析図は公式から引用

画像-20230224184421893

画像-20230224184508091

コードを解析する手順:

  1. コードを取得した後、V8 はストリーム入力を使用して字句解析を渡し、それをトークンに分析します。
  2. 解析/事前解析して実行ノードを 1 つずつ生成します
  3. ASTツリーの生成
  4. バイトコードに変換するためのホットスポット方法がある場合は、ターボファン コンパイラーを使用してそれを機械コードに最適化し、パフォーマンスを向上させます。

グローバルなコード実行プロセス

JS エンジンは、コードを実行する前にヒープ メモリにグローバル オブジェクトを作成します: Global Object (GO)

  • オブジェクトはすべてのスコープからアクセス可能です
  • これには、日付、配列、文​​字列、数値、setTimeout、setInterval などが含まれます。
  • それ自体を指す window 属性もあります

js エンジン内には実行コンテキスト スタック (Execution Context Stack、略して ECS) があり、これはコードを実行するために使用される呼び出しスタックです。

彼が実行するグ​​ローバル コード ブロックの機能は次のとおりです。

  • コードを実行するためにグローバル実行コンテキスト GEC グローバル コンテキストを構築する
  • この構築されたコンテキストを実行スタックに追加するということは、GEC を ECS に入れることを意味します。

GEC は ECS に配置され、次の 2 つの部分で構成されます。

  • コードが実行される前、パーサーを AST に変換するプロセス中に、グローバルに定義された変数、関数などが GlobalObject に追加されますが、値は割り当てられません。
  • コードの実行中に、変数に値を代入するか、他の関数を実行します。

各実行コンテキストは VO (Variable Object、変数オブジェクト) に関連付けられ、変数や関数の宣言はこの VO オブジェクトに追加され、グローバル コードが実行されると VO が GO オブジェクトになります。

グローバルコンテキストを理解するための 3 つの鍵:

  • VO(ゴー​​)
  • スコープチェーン
  • これ

以下のコード処理を実行します

    var message = "Global Message"
    function foo() {
    
    
        var message = "Foo Message"
    }
    var num1 = 10
    var num2 = 20
    var res = num1 + num2
    console.log(res);

グローバルコード実行前

画像-20230225230158035

コードを実行した後

画像-20230225231518618

関数コード実行処理

実行中に関数が実行されると、関数本体に基づいて関数実行コンテキスト (略して FEC) が作成され、EC スタックにプッシュされます。

  • 関数実行コンテキストに入ると、AO オブジェクト (Activation Object) が作成されます。
  • この AO オブジェクトは引数を使用して初期化され、初期値は渡されたパラメーターです。
  • この AO オブジェクトは、変数の初期化を保存する実行コンテキストの VO として使用されます。

以下の関数実行処理

実行前

画像-20230225231940365

処刑後

画像-20230225232044143

プロセスは次のとおりです。

  • 関数実行コンテキストである実行前に FEC を作成します。
  • 関数名を名前とする AO オブジェクトを作成します。
  • スコープチェーンの作成
  • コードを保存する関数オブジェクトを生成する
  • シビング (まだ何もありません)
  • 次にコードを上から下に実行します
  • 実行が完了すると、名前はundefineに変更されます。

スコープとスコープチェーン

実行コンテキストを入力すると、実行コンテキストはスコープ チェーン (スコープ チェーン) にも関連付けられます。

  • スコープ チェーンは、変数識別子の評価に使用されるオブジェクトのリストです。
  • 実行コンテキストに入ると、このスコープ チェーンが作成され、コード タイプに基づいて一連のオブジェクトが追加されます。

追伸: スコープが改善されます vo自体が無い場合は上位層に行って検索します 先に出力してから宣言するとunknownが出力されます これも確認されました。

スコープ改善演習
var n = 100
function foo(){
    
    
    n=200
}
foo()
console.log(n)

N =200

順次メモリ検索の図は次のとおりです。

  • グローバル コード作成関数は n を見つけて関数 vo に入れ、その後 foo() を呼び出します。
  • 関数呼び出し後の GO で n 個のコピーを検索する
  • 関数は終了し、n を出力します。

画像-20230226165920435

スコープ チェーンも JS クロージャの焦点です。JS のクロージャはスコープ チェーンを使用して、スコープ間で変数にアクセスできるようにします。これにより、開発効率が向上し、多くのトラブルが軽減されます。

おすすめ

転載: blog.csdn.net/doomwatcher/article/details/129270731