シャローコピーとディープコピー

JavaScript でオブジェクトや配列をコピーする際、シャローコピー(浅いコピー)とディープコピー(深いコピー)の違いを理解しておく必要があります。この違いを知らないと、意図せず元のデータを変更してしまうバグの原因になります。

参照の基本

JavaScript のオブジェクトと配列は参照型です。変数に代入しても、実際のデータへの「参照」がコピーされるだけで、データ自体は共有されます。

const original = { name: "田中" };
const reference = original;

reference.name = "山田";
console.log(original.name); // "山田"(元も変わってしまう)

シャローコピーとは

シャローコピーは、オブジェクトの1階層目のプロパティだけをコピーします。ネストしたオブジェクトや配列は参照のままです。

const original = { 
  name: "田中",
  address: { city: "東京" }
};

const shallow = { ...original };

shallow.name = "山田";
console.log(original.name); // "田中"(影響なし)

shallow.address.city = "大阪";
console.log(original.address.city); // "大阪"(影響を受ける!)
1階層目のプロパティ

コピーされるため、変更しても元に影響しない

ネストしたオブジェクト

参照が共有されるため、変更が元にも反映される

シャローコピーの方法

シャローコピーを作る代表的な方法は以下の通りです。

const obj = { a: 1, b: 2 };

// スプレッド構文
const copy1 = { ...obj };

// Object.assign()
const copy2 = Object.assign({}, obj);

// 配列の場合
const arr = [1, 2, 3];
const arrCopy1 = [...arr];
const arrCopy2 = arr.slice();

ディープコピーとは

ディープコピーは、ネストしたオブジェクトや配列も含めて完全に複製します。元のデータとの参照の共有は一切ありません。

const original = { 
  name: "田中",
  address: { city: "東京" }
};

const deep = JSON.parse(JSON.stringify(original));

deep.address.city = "大阪";
console.log(original.address.city); // "東京"(影響なし)

ディープコピーの方法

ディープコピーを作る主な方法を比較します。

JSON.parse(JSON.stringify())

最も簡単な方法ですが、関数、undefined、Date、Map、Set などは正しくコピーできません。

structuredClone()

モダンな方法で、Date や Map なども正しくコピーできます。ただし関数は含められません。

// structuredClone() を使う(推奨)
const original = { 
  date: new Date(),
  items: [1, 2, 3]
};

const deep = structuredClone(original);
console.log(deep.date instanceof Date); // true

用途に応じて適切なコピー方法を選択することが重要です。