JavaScript Tips | 配列ユーティリティ:ディープコピー

JavaScript JavaScript
スポンサーリンク

ディープコピーとは何か(まずここを正しく理解する)

ディープコピーは、配列やオブジェクトの“中身まで含めて完全に別物を作るコピー”のことです。
浅いコピー(shallow copy)と違い、ネストされた配列・オブジェクトもすべて独立した新しい値として複製されます

ここが最重要ポイントです。

浅いコピーは「配列の箱だけ別物、中身のオブジェクトは共有」。
ディープコピーは「箱も中身も全部別物」。

業務では、次のような場面でディープコピーが必要になります。

元データを絶対に壊したくない。
画面状態を更新するが、前の状態と完全に独立させたい。
API レスポンスを加工するが、元のレスポンスは保持したい。

浅いコピーで済ませると、気づかないうちに元データが書き換わる事故が起きます。
ディープコピーは、その事故を防ぐための“安全装置”です。


浅いコピーでは不十分な例(ディープコピーが必要な理由)

次のコードを見てください。

const a = [{ id: 1 }, { id: 2 }];
const b = a.slice(); // 浅いコピー

b[0].id = 999;

console.log(a[0].id); // 999(元の配列まで書き換わってしまう)
JavaScript

浅いコピーでは、配列そのものは別物になりますが、
中のオブジェクトは同じ参照のままです。

つまり、
b[0] と a[0] は「同じオブジェクト」を指している
→ b を変更すると a も変わる

これが業務で最も危険なバグの原因になります。


JSON を使った簡易ディープコピー(JSON データ限定)

JSON.parse(JSON.stringify(…)) を使う方法

配列の中身が 純粋な JSON データ(数値・文字列・真偽値・配列・プレーンオブジェクト)だけなら、
次の方法でディープコピーできます。

function deepCopyJson(array) {
  if (!Array.isArray(array)) {
    return [];
  }
  return JSON.parse(JSON.stringify(array));
}
JavaScript

動作例:

const a = [{ id: 1 }, { id: 2 }];
const b = deepCopyJson(a);

b[0].id = 999;

console.log(a[0].id); // 1(元データは無傷)
console.log(b[0].id); // 999
JavaScript

JSON 方式の注意点(ここが重要)

JSON 方式は便利ですが、次のものはコピーできません。

Date
Map / Set
関数
undefined
クラスインスタンス

これらが含まれる場合は、別の方法が必要です。


再帰を使った本格的ディープコピー(初心者でも理解できる形)

配列・オブジェクトを再帰的にコピーする

次は、業務で使える“本物のディープコピー”です。
配列・オブジェクトを再帰的にたどり、すべてを独立した値として複製します。

function deepCopy(value) {
  if (value === null || typeof value !== "object") {
    return value;
  }

  if (Array.isArray(value)) {
    const result = [];
    for (const item of value) {
      result.push(deepCopy(item));
    }
    return result;
  }

  const result = {};
  for (const key in value) {
    if (Object.prototype.hasOwnProperty.call(value, key)) {
      result[key] = deepCopy(value[key]);
    }
  }
  return result;
}
JavaScript

重要ポイントをかみ砕く

null やプリミティブ値はそのまま返す(コピー不要)。
配列なら、要素ごとに deepCopy を呼んで新しい配列を作る。
オブジェクトなら、キーごとに deepCopy を呼んで新しいオブジェクトを作る。
再帰(自分自身を呼ぶ)ことで、ネストが深くても全部コピーできる。

これで、配列の中にオブジェクトがあり、その中に配列があり…
という複雑な構造でも完全にコピーできます。


ディープコピーの動作例(理解が一気に深まる)

const original = [
  { id: 1, info: { score: 10 } },
  { id: 2, info: { score: 20 } }
];

const copied = deepCopy(original);

copied[0].info.score = 999;

console.log(original[0].info.score); // 10(元データは無傷)
console.log(copied[0].info.score);   // 999
JavaScript

浅いコピーでは絶対に防げない「中のオブジェクトの書き換え」が、
ディープコピーなら完全に防げます。


業務での具体的な使い方

画面状態の更新(React など)

React では「状態は不変に扱う」ことが基本です。

const nextState = deepCopy(prevState);
nextState.items.push(newItem);
JavaScript

浅いコピーだと、中のオブジェクトが共有されてしまい、
「変更検知が効かない」「再レンダリングされない」などの問題が起きます。

ディープコピーなら、状態が完全に独立するため安全です。


API レスポンスを加工する前にコピーする

API から返ってきたデータは「生データ」として保持しておきたいことが多いです。

const raw = await fetchData();
const viewData = deepCopy(raw);

// viewData を加工しても raw は壊れない
JavaScript

これにより、
「画面用の加工データ」と「元データ」を完全に分離できます。


ディープコピーで意識すべき重要ポイント

浅いコピーとディープコピーの違いを常に意識する

浅いコピーは「配列の箱だけ別、中身は共有」。
ディープコピーは「箱も中身も全部別」。

この違いを理解していないと、業務で必ずバグを生みます。


JSON 方式は便利だが万能ではない

JSON 方式は簡単ですが、Date や Map などが壊れます。
「純粋な JSON データだけ」のときに使う、と割り切るのが正解です。


再帰ディープコピーは万能だが、重い

再帰ディープコピーは強力ですが、データが大きいと重くなります。
業務では「本当にディープコピーが必要か?」を毎回考えることが大切です。


手を動かして理解を固める

次のコードをコンソールで実行してみてください。

function deepCopy(value) {
  if (value === null || typeof value !== "object") {
    return value;
  }
  if (Array.isArray(value)) {
    const result = [];
    for (const item of value) {
      result.push(deepCopy(item));
    }
    return result;
  }
  const result = {};
  for (const key in value) {
    if (Object.prototype.hasOwnProperty.call(value, key)) {
      result[key] = deepCopy(value[key]);
    }
  }
  return result;
}

const original = [{ id: 1, info: { score: 10 } }];
const copied = deepCopy(original);

copied[0].info.score = 999;

console.log(original[0].info.score); // 10
console.log(copied[0].info.score);   // 999
JavaScript

「浅いコピーでは防げない問題が、ディープコピーなら防げる」ことが体感できます。


まとめ:ディープコピーは“安全なデータ操作”の基礎

ディープコピーは、
元データを壊さずに安全に加工するための“基礎技術”です。

プロジェクトに次のような関数を置いておくと、
配列・オブジェクト操作の事故が激減します。

export function deepCopy(...) { ... }
JavaScript

浅いコピーとディープコピーを正しく使い分けられるようになると、
あなたの JavaScript コードは一段レベルアップします。

タイトルとURLをコピーしました