Back

Web を支える JavaScript エンジンの世界

Web を支える JavaScript エンジンの世界

あなたが書くすべての JavaScript コードは、実際に動作する前にエンジンを通過します。React アプリを構築する場合でも、Node.js サーバーを実行する場合でも、React Native モバイルアプリをリリースする場合でも、エンジンがコードを解析、コンパイル、実行しています。V8 JavaScript engine internals を理解し、SpiderMonkey vs V8 vs JavaScriptCore の違いを知ることで、より高速なコードを書き、謎めいたパフォーマンス問題をデバッグできるようになります。

重要なポイント

  • JavaScript エンジンは、パース、バイトコード生成、ジャストインタイム(JIT)コンパイルを通じて、ソースコードをマシン命令に変換します
  • V8 は Chrome、Edge、Node.js、Deno を支え、4段階のコンパイルパイプライン(Ignition、Sparkplug、Maglev、TurboFan)を持っています
  • SpiderMonkey(Firefox)、JavaScriptCore(Safari/Bun)、Hermes(React Native)は、それぞれ異なる優先事項に最適化されています:標準準拠、メモリ効率、起動速度
  • エンジンの選択は起動時間、メモリ使用量、ピークパフォーマンスに影響します。合成ベンチマークに頼らず、実際のターゲットプラットフォームでテストしましょう

JavaScript エンジンが実際に行うこと

JavaScript エンジンは、ソースコードをマシン命令に変換する特殊な仮想マシンです。このプロセスは予測可能なパターンに従います:コードを抽象構文木にパースし、バイトコードを生成し、ジャストインタイム(JIT)コンパイルを使用してホットパスを段階的に最適化します。

現代のエンジンは、単にコードを1行ずつ解釈するだけではありません。複数のコンパイル段階を使用し、高速だが最適化されていない実行から始め、頻繁に実行される関数を積極的な最適化で再コンパイルします。この多段階アプローチは、起動速度とピークパフォーマンスのバランスを取ります。

現代のランタイムを支える主要な JavaScript エンジン

V8:Chrome、Edge、Node.js、Deno を支える主力エンジン

V8 は JavaScript の世界を支配しています。Chrome、Microsoft Edge(2020年に Chakra から V8 に切り替え)、そして Node.js、Deno、Electron ランタイムを動かしています。

V8 のコンパイルパイプラインには4つの段階があります:

  • Ignition:素早く実行を開始するバイトコードインタープリター
  • Sparkplug:最適化されていないマシンコードを生成する高速ベースラインコンパイラ
  • Maglev:中間層の最適化コンパイラ(2023年追加)
  • TurboFan:ピークパフォーマンスのための最上位最適化コンパイラ

V8 は、アプリケーションをフリーズさせずにメモリを回収する並行ガベージコレクター Orinoco も使用しています。

SpiderMonkey:Firefox の標準準拠優先エンジン

SpiderMonkey は Mozilla のエンジンで、Firefox を動かしています。標準準拠を優先し、ECMAScript 機能を早期に実装します。

SpiderMonkey は独自の多段階システムを使用しています:ベースラインインタープリター、そして最適化コンパイルのための Warp(古い IonMonkey を置き換え)です。そのデバッグツールは Firefox DevTools と緊密に統合されており、深い可観測性が必要な開発者にとって価値があります。

JavaScriptCore:Apple のメモリ効率重視エンジン

JavaScriptCore(Nitro とも呼ばれる)は Safari とすべての iOS ウェブビューを動かしています。また、新しい JavaScript ランタイムである Bun のエンジンでもあります。

JavaScriptCore には4つの段階があります:LLInt(Low-Level Interpreter)、Baseline JIT、DFG(Data Flow Graph)、FTL(Faster Than Light)。Apple は、Safari が動作するモバイルデバイスにとって重要なメモリ効率とバッテリー寿命を重視して最適化しています。

Hermes:React Native のために構築されたエンジン

Hermes は Meta のエンジンで、現在 React Native のデフォルトです。ブラウザエンジンとは異なり、Hermes は事前(AOT)コンパイルを使用します:実行時ではなく、ビルド時に JavaScript をバイトコードに変換します。

このアプローチは、モバイルでのコールドスタートパフォーマンスを劇的に改善します。アプリはパースと初期コンパイルを完全にスキップし、代わりに事前コンパイルされたバイトコードをロードします。トレードオフとして、Hermes は完全な JIT を含みません。ピークスループットよりも起動速度とメモリフットプリントを最適化しています。

ランタイムがエンジンを選択する方法

Node.js、Deno、Bun ランタイムは、コードの動作に影響を与える異なるエンジン選択を行っています。

Node.js と Deno はどちらも V8 を組み込んでおり、優れたピークパフォーマンスと新しい ECMAScript 機能への高速アクセスを提供します。Bun は JavaScriptCore を選択し、ベンチマークでより高速な HTTP 処理を主張していますが、実際のパフォーマンスは特定のワークロードに大きく依存します。

React Native 開発者にとっては、Hermes とモバイル JavaScript エンジンが最も重要です。Hermes は APK サイズを削減し、time-to-interactive を改善します。これは低速デバイスでのユーザーエクスペリエンスに直接影響します。

これがあなたのコードにとって意味すること

これらのエンジンは十分に共通の動作を持っているため、ほとんどの JavaScript はどこでも同じように実行されます。しかし、境界では違いが現れます:

  • 起動時間:JavaScriptCore と Hermes はより速く起動し、V8 は持続的なスループットを最適化します
  • メモリ圧力:JavaScriptCore はメモリ使用量が少なく、モバイルでは重要です
  • セキュリティモード:一部の環境(特定のコンテキストでの iOS WKWebView など)では JIT コンパイルが完全に無効化され、インタープリターのみの実行にフォールバックします

WebAssembly は現在、すべての主要エンジンの成熟した統合部分であり、実験ではありません。Temporal API は現代のブラウザで実装されており、問題の多い Date オブジェクトを置き換えています。これらの機能は V8、SpiderMonkey、JavaScriptCore 全体で一貫して動作します。

ターゲットに基づいた選択

エンジンを直接選択することはほとんどありません。ブラウザまたはランタイムを選択すると、エンジンがそれに付属します。しかし、トレードオフを理解することで最適化に役立ちます:

  • サーバーサイドアプリを構築する場合は?Node.js と Deno(V8)は成熟したエコシステムを提供し、Bun(JavaScriptCore)は生の速度を優先します
  • Safari または iOS をターゲットにする場合は?JavaScriptCore のメモリ動作がアプリに影響します
  • React Native をリリースする場合は?Hermes がデフォルトです。その AOT モデルに最適化しましょう

実際のターゲットプラットフォームでテストしてください。エンジンチームからのベンチマーク数値は合成ワークロードを測定します。アプリのパフォーマンスは、特定のコードパターン、データ形状、ユーザーインタラクションに依存します。

まとめ

JavaScript エンジンは、あなたが構築するすべての Web およびモバイルアプリケーションの見えない基盤です。V8、SpiderMonkey、JavaScriptCore、Hermes はすべて標準 JavaScript を実行しますが、そのアーキテクチャの違い(コンパイル段階、メモリ管理戦略、最適化の優先順位)は意味のあるパフォーマンスの変動を生み出します。エンジンの内部を暗記するのではなく、どのエンジンがターゲットプラットフォームを動かしているかを理解し、それに応じてテストすることに焦点を当てましょう。最良の最適化戦略は、常に実際のデバイスで実際のユーザーワークロードを使用して実際のパフォーマンスを測定することです。

よくある質問

各ブラウザは独自の最適化戦略を持つ異なる JavaScript エンジンを使用しています。Chrome の V8 は持続的なスループットに優れ、Safari の JavaScriptCore はメモリ効率を優先し、Firefox の SpiderMonkey は標準準拠に焦点を当てています。これらのアーキテクチャの違いにより、同一のコードでも、それを実行するエンジンによって起動時間、メモリ使用量、ピークパフォーマンスが異なる可能性があります。

一般的には、いいえ。ベストプラクティスに従ったクリーンで慣用的な JavaScript を書けば、すべてのエンジンがうまく処理します。特定のエンジン向けに最適化するのは、プロファイリングでターゲットプラットフォーム上の実際のボトルネックが明らかになった場合のみです。時期尚早なエンジン固有の最適化は、エンジンが内部を更新したり、コードが予期しない環境で実行されたりすると、しばしば裏目に出ます。

ジャストインタイムコンパイルは、事前ではなく、プログラムの実行中に JavaScript をマシンコードに変換します。エンジンが JIT を使用するのは、JavaScript が動的であり、型情報は実行中にのみ明らかになるためです。どの関数が頻繁に実行され、どのような型を受け取るかを観察することで、JIT コンパイラはホットパス用に高度に最適化されたマシンコードを生成しながら、起動を高速に保ちます。

Hermes は事前コンパイルを使用し、実行時ではなくビルド時に JavaScript をバイトコードに変換します。これにより、アプリ起動時のパースと初期コンパイルが不要になり、モバイルデバイスでのコールドスタートパフォーマンスが劇的に改善されます。Hermes はまた、より小さなバンドルサイズを生成し、メモリ使用量も少なくなります。これはリソースが制約された携帯電話では重要です。

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay