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)となるオブジェクト

  1. 関数・メソッド(Function, アロー関数)
  2. DOM ノード(document.getElementById() 等の HTML 要素)
  3. プロトタイプチェーン(クラスのインスタンスメソッドはプレーンオブジェクト化する)
  4. ゲッター / セッター プロパティ

安全な使い分け手順

  1. 状態(State)やデータオブジェクトのコピーには structuredClone() を第一選択とする
  2. クラスのメソッドや関数が含まれるオブジェクトのコピーには、lodash.cloneDeep や手動ファクトリ関数を使用する
  3. 入力データに不可知なオブジェクトが含まれる可能性がある場合は try...catch で囲む
// ⭕ DataCloneError を考慮した安全な実装パターン
function safeDeepClone(data) {
  try {
    return structuredClone(data);
  } catch (err) {
    if (err.name === 'DataCloneError') {
      console.warn('関数やDOM要素が含まれているためフォールバック処理を実行します');
    }
    throw err;
  }
}