JavaScriptオブジェクトディープコピー: structuredCloneとJSON.parseの比較
JavaScript Frontend Performance
結論
ディープコピーにはネイティブの structuredClone() を使い、関数を含む場合は注意します。
// MDN公式仕様:structuredClone() の使用例
const original = {
date: new Date(),
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3]),
};
// ⭕ ネイティブ型を維持したディープコピー
const cloned = structuredClone(original);
console.log(cloned.date instanceof Date); // true
MDN公式仕様:2つのディープコピー手法比較
| 評価軸 | structuredClone() (Web標準API) | JSON.parse(JSON.stringify()) |
|---|---|---|
| 循環参照 | ◯ 正しく複製可能 | × TypeError クラッシュ |
Date オブジェクト | ◯ Dateインスタンスとして保持 | × ISO文字列(string)へ勝手に変換 |
Map / Set | ◯ 完全複製 | × 空のオブジェクト {} へ消滅 |
undefined / Symbol | ◯ プロパティを保持 | × キーごと削除される |
| パフォーマンス | 高速(C++層で最適化) | 文字列化・パースの2重負荷 |
実際に起こる事故:DataCloneError の発生条件
MDN公式ドキュメント(Structured Clone Algorithm)で規定されている通り、structuredClone() は万能ではなく、クローン不可能なオブジェクトを渡すと DOMException: DataCloneError を発生させて処理を停止させます。
クローン不可(DataCloneError)となるオブジェクト
- 関数・メソッド(
Function, アロー関数) - DOM ノード(
document.getElementById()等の HTML 要素) - プロトタイプチェーン(クラスのインスタンスメソッドはプレーンオブジェクト化する)
- ゲッター / セッター プロパティ
安全な使い分け手順
- 状態(State)やデータオブジェクトのコピーには
structuredClone()を第一選択とする - クラスのメソッドや関数が含まれるオブジェクトのコピーには、
lodash.cloneDeepや手動ファクトリ関数を使用する - 入力データに不可知なオブジェクトが含まれる可能性がある場合は
try...catchで囲む
// ⭕ DataCloneError を考慮した安全な実装パターン
function safeDeepClone(data) {
try {
return structuredClone(data);
} catch (err) {
if (err.name === 'DataCloneError') {
console.warn('関数やDOM要素が含まれているためフォールバック処理を実行します');
}
throw err;
}
}