はじめに
この記事を読めばJavaScriptディープコピーを簡単に作成・利用できるようになります。
ディープコピーの基本から作り方、使い方、注意点、カスタマイズ方法まで徹底解説し、サンプルコードと応用例を交えて分かりやすく説明します。
JavaScript初心者でも安心して読める内容ですので、ぜひ最後までお読みください。
●JavaScriptディープコピーとは
○ディープコピーの基本
ディープコピーとは、オブジェクトや配列などのデータ構造を完全に複製することです。
シャローコピー(浅いコピー)とは異なり、ディープコピーでは元のデータ構造と複製したデータ構造が独立しています。
つまり、複製したデータ構造を変更しても、元のデータ構造に影響を与えません。
●ディープコピーの作り方
○サンプルコード1:JSON.parse()とJSON.stringify()を使ったディープコピー
このコードではJSON.parse()とJSON.stringify()を使ってディープコピーを実現します。
この例ではオブジェクトをJSON形式の文字列に変換し、その文字列を再度パースして新しいオブジェクトを生成しています。
const original = {
a: 1,
b: {
c: 2
}
};
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.b.c = 3;
console.log(original.b.c); // 2
console.log(deepCopy.b.c); // 3
○サンプルコード2:再帰関数を使ったディープコピー
このコードでは再帰関数を使ってディープコピーを実現します。
この例では各プロパティを順番にコピーし、プロパティがオブジェクトや配列の場合は再帰的にコピーを行っています。
function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const result = Array.isArray(obj) ? [] : {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
result[key] = deepCopy(obj[key]);
}
}
return result;
}
const original = {
a: 1,
b: {
c: 2
}
};
const deepCopy = deepCopy(original);
deepCopy.b.c = 3;
console.log(original.b.c); // 2
console.log(deepCopy.b.c); // 3
●ディープコピーの使い方
○サンプルコード3:ディープコピーを使った配列のコピー
このコードではJSON.parse()とJSON.stringify()を使って、配列をディープコピーする方法を紹介しています。
この例では元の配列とコピーされた配列が独立していることを確認できます。
const originalArray = [
{
a: 1
},
{
b: 2
}
];
const copiedArray = JSON.parse(JSON.stringify(originalArray));
copiedArray[0].a = 3;
console.log(originalArray[0].a); // 1
console.log(copiedArray[0].a); // 3
○サンプルコード4:ディープコピーを使ったオブジェクトのコピー
このコードでは再帰関数を使って、オブジェクトをディープコピーする方法を紹介しています。
この例ではオブジェクト内のプロパティが参照型の場合でも、元のオブジェクトとコピーされたオブジェクトが独立していることを確認できます。
function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const result = Array.isArray(obj) ? [] : {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
result[key] = deepCopy(obj[key]);
}
}
return result;
}
const originalObject = {
a: 1,
b: {
c: 2
}
};
const copiedObject = deepCopy(originalObject);
copiedObject.b.c = 3;
console.log(originalObject.b.c); // 2
console.log(copiedObject.b.c); // 3
●ディープコピーの応用例
○サンプルコード5:ディープコピーを使った状態管理
このコードではディープコピーを使って、状態管理を行う方法を紹介しています。
この例では状態オブジェクトをディープコピーして更新することで、状態の変更が他の部分に影響を与えないことを確認できます。
function updateState(state, newState) {
return {
...deepCopy(state),
...newState
};
}
const initialState = {
a: 1,
b: {
c: 2
}
};
const newState = updateState(initialState, { a: 3, b: { c: 4 } });
console.log(initialState); // { a: 1, b: { c: 2 } }
console.log(newState); // { a: 3, b: { c: 4 } }
○サンプルコード6:ディープコピーを使った履歴機能の実装
このコードではディープコピーを使って、オブジェクトの編集履歴を保存し、元に戻す機能を実装する方法を紹介しています。
この例では編集履歴の配列にディープコピーされたオブジェクトを追加し、元に戻す際には最後の履歴を取り出すことで実現しています。
class History {
constructor() {
this.history = [];
}
add(obj) {
this.history.push(deepCopy(obj));
}
undo() {
return this.history.pop();
}
}
const obj = { a: 1 };
const history = new History();
history.add(obj);
obj.a = 2;
history.add(obj);
obj.a = 3;
console.log(obj); // { a: 3 }
obj = history.undo();
console.log(obj); // { a: 2 }
obj = history.undo();
console.log(obj); // { a: 1 }
●注意点と対処法
○循環参照の問題
ディープコピーを実装する際には、循環参照が発生する可能性があることに注意が必要です。
循環参照が発生した場合、再帰的なディープコピーが無限ループに陥ることがあります。
これを回避するために、既にコピーされたオブジェクトをキャッシュしておく方法があります。
○パフォーマンスの問題
ディープコピーは、オブジェクトの構造が複雑であるほど、実行時間が長くなります。
パフォーマンスが重要な場合は、必要最低限のディープコピーを行うか、他の方法を検討することが望ましいです。
●ディープコピーのカスタマイズ方法
○サンプルコード7:特定のプロパティだけディープコピーする方法
このコードでは、特定のプロパティだけディープコピーする方法を紹介しています。
この例ではオブジェクトのプロパティb
のみディープコピーを行い、他のプロパティはシャローコピーしています。
function customDeepCopy(obj, propertiesToDeepCopy) {
const result = {};
for (const key in obj) {
if (propertiesToDeepCopy.includes(key)) {
result[key] = deepCopy(obj[key]);
} else {
result[key] = obj[key];
}
}
return result;
}
const originalObject = {
a: 1,
b: {
c: 2
}
};
const copiedObject = customDeepCopy(originalObject, ['b']);
copiedObject.b.c = 3;
console.log(originalObject.b.c); // 2
console.log(copiedObject.b.c); // 3
○サンプルコード8:ディープコピー時にカスタムロジックを適用する方法
このコードでは、ディープコピー時にカスタムロジックを適用する方法を紹介しています。
この例では、ディープコピーする際にプロパティの値が10より大きい場合、値を10に制限しています。
function customDeepCopyWithLogic(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
const result = Array.isArray(obj) ? [] : {};
for (const key in obj) {
const value = obj[key];
result[key] = typeof value === 'number' && value > 10 ? 10 : customDeepCopyWithLogic(value);
}
return result;
}
const originalObject = {
a: 5,
b: {
c: 15
}
};
const copiedObject = customDeepCopyWithLogic(originalObject);
console.log(copiedObject); // { a: 5, b: { c: 10 } }
まとめ
この記事では、JavaScriptにおけるディープコピーの基本概念、作成方法、利用方法、応用例、注意点、そしてカスタマイズ方法を紹介しました。
ディープコピーを適切に使用することで、データの編集や状態管理を安全かつ効率的に行うことができます。
ただし、循環参照やパフォーマンスの問題に注意して、適切な方法でディープコピーを実装することが重要です。