「JavaScriptCoreって何?」「SafariではどうやってJavaScriptが動いているの?」「V8エンジンとの違いは?」
こんな疑問を持ったことはありませんか。
JavaScriptCoreは、SafariブラウザやiOSアプリでJavaScriptコードを実行するためのエンジンです。
この記事では、JavaScriptCoreの基本的な概念から内部の仕組み、実際の使い方まで詳しく解説します。
プログラミング初心者の方でも理解できるように、専門用語も丁寧に説明していきます。
JavaScriptCoreとは
JavaScriptCoreは、Appleが開発したJavaScriptエンジン(JavaScript実行環境)です。
NitroやSquirrelFishという別名でも知られています。
JavaScriptエンジンとは
JavaScriptエンジンとは、JavaScriptのコードを解析して実行するためのプログラムです。
人間が書いたJavaScriptのコード(テキスト)を、コンピュータが理解できる命令に変換して実行します。
例えば、Webページで「ボタンをクリックしたらポップアップを表示する」といった動作は、JavaScriptエンジンが裏で動いているおかげで実現できています。
JavaScriptCoreの位置づけ
JavaScriptCoreは、WebKitというWebブラウザエンジンの一部として開発されています。
WebKitは、Webページを画面に表示するための仕組み全体を指し、その中でJavaScript部分を担当しているのがJavaScriptCoreです。
主な使用例:
Safari(macOSとiOSのWebブラウザ)で使われています。
Mail(メールアプリ)やApp Store(アプリストア)などのApple製アプリでも使われています。
iOS/macOSアプリの開発者が、アプリ内でJavaScriptを実行するために使えます。
他のJavaScriptエンジンとの違い
代表的なJavaScriptエンジンには、以下のようなものがあります。
V8: GoogleのChromeブラウザとNode.jsで使われているエンジンです。
SpiderMonkey: Mozillaが開発し、Firefoxで使われているエンジンです。
Chakra: Microsoftが開発し、以前のEdgeブラウザで使われていたエンジンです(現在のEdgeはV8を使用)。
それぞれのエンジンは、JavaScriptを実行するという同じ目的を持ちながらも、内部の仕組みや最適化の方法が異なります。
JavaScriptCoreの歴史
JavaScriptCoreは1998年にAppleによって開発されました。
もともとはMac OS Xの一部として作られ、その後2005年にWebKitプロジェクトの一部としてオープンソース化されました。
主な進化の歴史:
1998年: Mac OS Xの一部として開発開始しました。
2005年: WebKitプロジェクトとしてオープンソース化されました。
2008年: SquirrelFish(高速化版)がリリースされました。
2008年: SquirrelFish Extreme(さらなる高速化版)がリリースされました。
2013年: iOS 7.0とOS X 10.9でJavaScriptCore.frameworkが利用可能になりました。
現在では、継続的に改良が加えられ、パフォーマンスと機能が向上し続けています。
JavaScriptCoreの仕組み
JavaScriptCoreがどのようにしてJavaScriptコードを実行しているのか、その内部の仕組みを見ていきましょう。
4層のコンパイルシステム
JavaScriptCoreは、コードの実行速度を最適化するために、4つの層(ティア)を使い分けています。
実行回数が少ないコードは簡単な方法で実行し、何度も実行されるコード(ホットコード)は高度な最適化を行います。
第1層: LLInt(Low Level Interpreter):
JavaScriptコードを最初に実行する際に使われます。
起動速度を重視し、すぐに実行を開始できます。
バイトコード(中間コード)をそのまま実行します。
第2層: Baseline JIT:
関数が一定回数(約60回)実行されると、この層に移行します。
JIT(Just-In-Time)コンパイラが、バイトコードを機械語(コンピュータが直接実行できるコード)に変換します。
LLIntよりも高速に実行できます。
第3層: DFG JIT(Data Flow Graph JIT):
関数がさらに多く実行される(約1000回のループなど)と、この層に移行します。
データフローグラフを使って、より高度な最適化を行います。
型情報を活用して、不要なチェックを省略できます。
第4層: FTL JIT(Faster Than Light JIT):
非常に頻繁に実行される関数(数千回〜数万回)に対して使われます。
LLVM(最適化コンパイラ基盤)を使って、最高レベルの最適化を行います。
最も高速に実行できますが、最適化に時間がかかります。
コード実行の流れ
JavaScriptコードが実行されるまでの流れを、順を追って説明します。
1. 字句解析(Lexer):
JavaScriptのソースコード(テキスト)を読み込みます。
コードをトークン(単語のようなもの)に分解します。
例: let x = 10; → let, x, =, 10, ;という5つのトークンに分解します。
2. 構文解析(Parser):
トークンの並びから、構文木(Abstract Syntax Tree: AST)を作ります。
構文木は、コードの構造を木のような形で表現したものです。
文法エラーがあれば、この段階で検出されます。
3. バイトコード生成:
構文木から、バイトコードという中間コードを生成します。
バイトコードは、機械語よりは人間に読みやすく、ソースコードよりは実行しやすい形式です。
4. 実行と最適化:
最初はLLIntでバイトコードを実行します。
実行回数に応じて、Baseline JIT → DFG JIT → FTL JITと段階的に最適化されます。
プロファイリング情報(型情報や実行頻度など)を収集し、それを元に最適化を行います。
型推論と最適化
JavaScriptは動的型付け言語なので、変数の型は実行時まで決まりません。
しかし、実際には同じ変数はほぼ同じ型で使われることが多いです。
JavaScriptCoreは、この特性を利用して最適化を行います。
プロファイリング:
LLIntとBaseline JITは、コードの実行中に型情報を収集します。
「この変数は常に数値として使われている」といった情報を記録します。
投機的最適化:
DFG JITとFTL JITは、収集された型情報を元に「この変数は数値だろう」と推測します。
その推測に基づいて、型チェックを省略した高速なコードを生成します。
デオプティマイゼーション(最適化解除):
もし推測が外れた場合(数値だと思っていた変数に文字列が入ってきた場合など)、最適化されたコードから元のコードに戻ります。
これをOSR Exit(On-Stack Replacement Exit)と呼びます。
メモリ管理
JavaScriptCoreは、ガベージコレクション(自動メモリ管理)を行います。
マークアンドスイープ方式:
使用中のオブジェクトに印(マーク)をつけます。
マークされていないオブジェクト(もう使われていないオブジェクト)をメモリから削除します。
世代別ガベージコレクション:
新しく作られたオブジェクトと、長く生き残っているオブジェクトを分けて管理します。
新しいオブジェクトは頻繁にチェックし、古いオブジェクトは時々チェックすることで、効率を上げます。
JavaScriptCoreの特徴
JavaScriptCoreには、他のJavaScriptエンジンと比較して以下のような特徴があります。
起動時間の速さ
JavaScriptCoreは、起動時間(コードの実行を開始するまでの時間)を重視した設計になっています。
LLIntは、構文解析の後すぐに実行を開始できます。
複雑な最適化は、コードが何度も実行された後に行われます。
Webページを開いた瞬間の応答性が良くなります。
メモリ効率
Appleのデバイス(iPhone、iPadなど)はバッテリー駆動なので、メモリ使用量を抑えることが重要です。
JavaScriptCoreは、V8エンジンと比べてメモリ使用量が少ない傾向があります。
バッテリー消費を抑えることができます。
多層JITコンパイル
4つの異なる層を使い分けることで、柔軟な最適化が可能です。
軽いコードは軽い処理で実行できます。
重いコード(頻繁に実行されるコード)だけを徹底的に最適化できます。
全体として、バランスの取れたパフォーマンスを実現できます。
Apple製品との統合
Appleのエコシステムに深く統合されています。
Safariブラウザで最適化されています。
iOS/macOSアプリから簡単に利用できます。
SwiftやObjective-Cとの連携がスムーズです。
V8エンジンとの比較
GoogleのV8エンジンと比較すると、以下のような違いがあります。
設計思想の違い
JavaScriptCore:
起動時間を重視しています。
メモリ効率を重視しています。
段階的な最適化(4層)を行います。
V8:
実行速度(ピークパフォーマンス)を重視しています。
積極的な最適化を行います。
より少ない層(2〜3層)で最適化を行います。
使用場面の違い
JavaScriptCore:
Safariブラウザで使われます。
iOS/macOSアプリで使われます。
Bunという新しいJavaScriptランタイムでも使われています。
V8:
Google Chromeブラウザで使われます。
Node.js(サーバーサイドJavaScript)で使われます。
Electron(デスクトップアプリフレームワーク)で使われます。
パフォーマンスの特性
JavaScriptCore:
起動が速いです。
メモリ使用量が少ないです。
長時間実行されるコードでは、V8とほぼ同等のパフォーマンスを発揮します。
V8:
ピーク時のパフォーマンスが高いです。
サーバーサイドアプリケーションに適しています。
メモリを多く使う傾向があります。
JavaScriptCoreの使い方
開発者がJavaScriptCoreを実際に使う方法を紹介します。
iOS/macOSアプリでの利用
iOS 7.0とOS X 10.9以降では、JavaScriptCore.frameworkを使ってアプリ内でJavaScriptを実行できます。
基本的な使い方(Swift):
import JavaScriptCore
// JavaScriptコンテキストを作成
let context = JSContext()!
// JavaScriptコードを実行
let result = context.evaluateScript("1 + 2 + 3")
// 結果を取得
print(result?.toInt32() ?? 0) // 6
ネイティブコードとの連携
JavaScriptとSwift/Objective-Cのコードを相互に呼び出すことができます。
Swift関数をJavaScriptから呼び出す:
import JavaScriptCore
let context = JSContext()!
// Swift関数を定義
let doubleFunction: @convention(block) (Int) -> Int = { value in
return value * 2
}
// JavaScriptコンテキストに登録
context.setObject(doubleFunction, forKeyedSubscript: "double" as NSString)
// JavaScriptから呼び出す
let result = context.evaluateScript("double(5)")
print(result?.toInt32() ?? 0) // 10
JavaScriptの関数をSwiftから呼び出す:
import JavaScriptCore
let context = JSContext()!
// JavaScript関数を定義
context.evaluateScript("""
function add(a, b) {
return a + b;
}
""")
// JavaScript関数を取得
let addFunction = context.objectForKeyedSubscript("add")
// Swiftから呼び出す
let result = addFunction?.call(withArguments: [5, 3])
print(result?.toInt32() ?? 0) // 8
エラーハンドリング
JavaScriptコードで例外が発生した場合、エラーハンドラーで処理できます。
import JavaScriptCore
let context = JSContext()!
// エラーハンドラーを設定
context.exceptionHandler = { context, exception in
if let error = exception {
print("JavaScriptエラー: \(error)")
}
}
// 文法エラーのあるコードを実行
context.evaluateScript("**INVALID**")
// 出力: JavaScriptエラー: SyntaxError: Unexpected token '**'
実用的な活用例
アプリ内でカスタムスクリプトを実行:
ユーザーが独自のスクリプトを書いて、アプリの動作をカスタマイズできます。
ビルドせずにコードを更新できるため、アプリストアの審査を待たずに機能追加できます。
データ処理:
複雑なビジネスロジックをJavaScriptで記述し、複数のプラットフォーム(iOS/Android/Web)で共有できます。
動的なUI生成:
JavaScriptでUI構造を記述し、ネイティブコードで描画することで、柔軟なUIを実現できます。
JavaScriptCoreを使う際の注意点
パフォーマンスの考慮
JavaScriptとネイティブコード間の呼び出しには、オーバーヘッド(余分な処理時間)がかかります。
頻繁に呼び出す必要がある処理は、ネイティブコードで実装した方が高速です。
メモリ管理
JavaScriptとネイティブコードの間でオブジェクトを共有する際は、循環参照に注意が必要です。
適切にメモリ管理を行わないと、メモリリークが発生する可能性があります。
スレッドセーフティ
JSContextは、作成されたスレッドでのみ使用できます。
別のスレッドから操作する場合は、適切な同期処理が必要です。
デバッグ
Safariの開発者ツールを使って、JavaScriptコードをデバッグできます。
Xcodeで実行中のアプリに対して、Safariからデバッガーを接続できます。
JavaScriptCoreの今後
JavaScriptCoreは、現在も活発に開発が続けられています。
継続的なパフォーマンス改善
新しい最適化技術が定期的に追加されています。
ECMAScript(JavaScriptの標準仕様)の新しい機能のサポートが追加されています。
WebAssemblyのサポート
WebAssembly(高速に実行できるバイナリ形式のコード)のサポートが強化されています。
C/C++などの言語で書かれたコードを、JavaScriptCoreで実行できます。
クロスプラットフォーム開発への対応
BunなどのJavaScriptランタイムでJavaScriptCoreが採用されるなど、Apple製品以外での利用も広がっています。
まとめ
JavaScriptCoreは、Appleが開発したJavaScriptエンジンで、Safariブラウザを始めとする多くのApple製品で使われています。
主な特徴:
4層のJITコンパイルシステムで段階的に最適化を行います。
起動時間の速さとメモリ効率を重視した設計になっています。
iOS/macOSアプリでJavaScriptを実行できるJavaScriptCore.frameworkを提供しています。
ネイティブコードとJavaScriptコードを相互に呼び出せます。
V8エンジンとの違い:
JavaScriptCoreは起動時間とメモリ効率を重視し、V8は実行速度を重視しています。
JavaScriptCoreはApple製品に最適化され、V8はChrome、Node.js、Electronで使われています。
開発者にとってのメリット:
アプリ内でJavaScriptを実行できるため、動的な機能追加が可能です。
複数のプラットフォームでビジネスロジックを共有できます。
SwiftやObjective-Cとの連携がスムーズです。
JavaScriptCoreを理解することで、Safariがどのように動いているのか、iOS/macOSアプリでどのようにJavaScriptを活用できるのかが分かります。
特にApple製品向けの開発を行う方にとって、JavaScriptCoreは強力なツールとなります。
参考情報
本記事は以下の信頼できる情報源を参照して作成しました。
- JavaScriptCore – WebKit Documentation
- JavaScriptCore – Apple Developer Documentation
- JavaScriptCore – WebKit
- List of JavaScript engines – Wikipedia
- JavaScript Core and V8 A Deep Dive into Engine Architecture and Performance
この記事は2025年2月9日時点の情報に基づいています。

コメント