ブラウザの動作原理とV8エンジン
ブラウザカーネルとJSエンジンの関係
- WebCore: HTMLの解析、レイアウト、レンダリングなどの関連作業を担当
- JavaScriptCore: JavaScriptコードの解析と実行を担当
V8エンジンの原理
- JavaScriptソースコード
- 字句解析: ソースコード内の各単語のタイプ、値などの情報を解析
- 構文解析: 単語のタイプ情報に基づいて構文解析を行い、抽象構文木を生成
- AST抽象構文木(形式が固定されたツリー構造のオブジェクト)
- MachineCode最適化されたマシンコード(複数回実行されるバイトコード関数はホット関数としてマークされ、固定のマシン命令セットに最適化されます)
- バイトコード
JavaScriptの実行プロセス
グローバルコードの実行とスコープの昇格
- 実行コンテキストスタック(ECStack)
- グローバル実行コンテキスト(GEC)
- 変数オブジェクト(VO) == グローバルオブジェクト(GO)
- 関数実行コンテキスト(FEC)
- 変数オブジェクト(VO) == 活性オブジェクト(AO)
- スコープチェーン == AO + 親スコープ + GO
- 宣言の昇格: 変数の代入や関数の実行が行われる前のプロセス
- 遅延解析: グローバルスコープで実行される関数を事前に解析し、ネストされた関数は呼び出されたときに完全に解析される
JavaScriptのメモリ管理とクロージャ
一般的なGCアルゴリズム
クロージャ
- 高階関数: 関数のパラメータまたは戻り値が関数である場合、その関数は高階関数と呼ばれます
- クロージャ: 関数 + アクセス可能な自由変数
JavaScriptにおけるthisについて
グローバルスコープにおけるthis
- ブラウザ: thisはwindowを指す
- Node環境: thisは空のオブジェクト({})を指す
thisの4つのバインディングルール
- デフォルトバインディング(fun()): 独立した関数呼び出しではthisはwindowを指す
- 暗黙的バインディング(object.fun()): objectオブジェクトがjsエンジンによってfun関数内のthisにバインドされる
- 明示的バインディング:
- apply: fun.apply(object,[itemX])
- call: fun.call(object,itemX)
- bind: var funBind = fun.bind(object); funBind()
- newバインディング: function Person(name) { this.name = name; } thisはコンストラクタnewによって作成されたオブジェクトを指す
- 優先順位: newバインディング > 明示的バインディング(bind > call/apply) > 暗黙的バインディング > デフォルトバインディング
関数型プログラミング
純粋関数
- 同じ入力は必ず同じ出力を生成する
- 実行過程で副作用を一切生じない
カリー化
- カリー化の利点: 上位層の関数ロジックの再利用
- 簡略化した記述: var fun = x => y => z => x + y + z
汎用カリー化関数の実装
function autoCurrying(fn) {
return function curried(...args1) {
if (args1.length >= fn.length) {
return fn.apply(this, args1);
} else {
return function (...args2) {
return curried.apply(this, args1.concat(args2));
};
}
};
}
function logMessage(time, level, message) {
console.log(`[${time.getHours()}:${time.getMinutes()}][${level}][${message}]`);
}
const curriedLogger = autoCurrying(logMessage);
curriedLogger(new Date())("DEBUG")("これは日本語のメッセージです");
curriedLogger(new Date(), "DEBUG")("これは日本語のメッセージです");
curriedLogger(new Date(), "DEBUG", "これは日本語のメッセージです");
合成関数
- 合成関数: 順次実行される複数の関数を一つにまとめる
汎用合成関数の実装
function compose(...fns) {
const length = fns.length;
for (let i = 0; i < length; i++) {
if (typeof fns[i] !== "function") {
throw new TypeError("引数は関数である必要があります");
}
}
return function (...args) {
let result = length ? fns[0].apply(this, args) : args;
for (let i = 1; i < length; i++) {
result = fns[i].call(this, result);
}
return result;
};
}
function add(a, b) {
return a + b;
}
function multiplyByTwo(c) {
return c * 2;
}
const composedFn = compose(add, multiplyByTwo);
console.log(composedFn(1, 2)); // 6 (1+2=3, 3*2=6)
ES6クラス(class)
クラスとコンストラクタ関数
- クラスはコンストラクタ関数のシンタックスシュガーであり、本質的な違いはない
クラスのコンストラクタ
- クラスにはコンストラクタが一つしか存在できない
- newキーワードでクラスを呼び出すと、新しいオブジェクトが作成され、thisがそのオブジェクトを指す
クラス内のメソッド
- インスタンスメソッド: クラスのプロトタイプに定義される
- アクセッサーメソッド: get/setキーワードを使用してプロパティのアクセスと設定を制御
- 静的メソッド: staticキーワードを使用してクラス自体に関連付けられる
クラスの継承
- extendsキーワードを使用して親クラスを継承
- super()を使用して親クラスのコンストラクタを呼び出す
- メソッドのオーバーライド: 子クラスで親クラスのメソッドを再定義
ES6からES5への変換
- Babel: ES6+のコードをES5に変換するツール
- #__PURE__#: 純粋関数をマークする。webpackのtree-shakingで未使用のコードを削除するために使用される
ES6の新機能
リテラルの強化された記述法
- プロパティの省略記法: {name, age}
- メソッドの省略記法: { method() {} }
- 計算されたプロパティ名: [dynamicKey]
分割代入(Destructuring)
- 配列の分割代入: const [a, b] = [1, 2]
- オブジェクトの分割代入: const {name, age} = person
- デフォルト値: const {name = "default"} = obj
テンプレート文字列
li>複数行文字列のサポート
- 式の埋め込み: `${expression}`
- タ付きテンプレート: 関数をテンプレートの前に配置
デフォルト引数値
li>関数の引数にデフォルト値を設定: function fn(param = "default")
- デフォルト値を持つ引数は関数のlengthプロパティに含まれない
残余引数(Rest Parameters)
- 不定数の引数を配列として受け取る: function fn(...args)
- argumentsオブジェクトとの違い: 残余引数は実際の配列であり、アロー関数でも使用可能
アロー関数
- 簡潔な関数構文: () => {}
- 独自のthisを持たず、レキシカルスコープからthisを継承
- コンストラクタとして使用できない
展開構文(Spread Syntax)
- 関数呼び出し時: fn(...array)
- 配列リテラル: [...array1, ...array2]
- オブジェクトリテラル: {...obj1, ...obj2}
ES6の新しいデータ型とデータ構造
Symbol
- 一意な値を表す新しい原始データ型
- オブジェクトのプロパティキーとして使用可能
- Symbol.for()でグローバルシンボルを登録・取得
Set
- 重複を許さない値のコレクション
- add(), delete(), has(), clear()などのメソッド
- forEach()やfor...ofで反復可能
WeakSet
- オブジェクトのみを格納できるSetの弱い参照版
- ガベージコレクションによるオブジェクトの自動解放
- 反復やサイズの取得は不可
Map
- キーと値のペアのコレクション
- 任意のデータ型をキーとして使用可能
- Setと同様のメソッドをサポート
WeakMap
- オブジェクトのみをキーとして使用できるMapの弱い参照版
- ガベージコレクションによるキーオブジェクトの自動解放
- 反復やサイズの取得は不可
ES7~ES12の新機能
- ES7: Array.includes(), 指数演算子(**)
- ES8: Object.values(), Object.entries(), String.padStart(), String.padEnd()
- ES9: Promise.finally(), Object.fromEntries()
- ES10: Array.flat(), Array.flatMap(), trimStart(), trimEnd()
- ES11: BigInt, Nullish Coalescing(??), Optional Chaining(?.), Global This
- ES12: FinalizationRegistry, WeakRef, 論理代入演算子(||=, &&=, ??=)