ディープコピーとは何か(まずここを正しく理解する)
ディープコピーは、配列やオブジェクトの“中身まで含めて完全に別物を作るコピー”のことです。
浅いコピー(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
JavaScriptJSON 方式の注意点(ここが重要)
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 コードは一段レベルアップします。
