オブジェクトのコピーって何者?
「オブジェクトのコピー」は、
「元のオブジェクトとは独立した“もう一つのオブジェクト”を作ること」 です。
ここで一番ハマりやすい落とし穴が、
const original = { name: "太郎" };
const copy = original; // これは“コピー”ではない
JavaScriptこの代入は「コピー」ではなく、
「同じオブジェクトを指す別名を作っているだけ」 です。
だから、こうなります。
const original = { name: "太郎" };
const copy = original;
copy.name = "花子";
console.log(original.name); // 花子(元も変わる)
console.log(copy.name); // 花子
JavaScript「コピーしたつもりで片方を変えたら、元も変わってしまった」
これが、オブジェクトコピーで最初にぶつかる壁です。
代入がダメなら、どうやってコピーするのか?
「シャローコピー」という基本のコピー
現代の JavaScript で、
一番よく使われるコピー方法はこの2つです。
const original = { name: "太郎", age: 25 };
// スプレッド構文
const copy1 = { ...original };
// Object.assign
const copy2 = Object.assign({}, original);
JavaScriptどちらも「original と同じプロパティを持つ“別のオブジェクト”」を作ります。
copy1.name = "花子";
console.log(original.name); // 太郎(元は変わらない)
console.log(copy1.name); // 花子
JavaScriptここで重要なのは、
「これは“シャローコピー(浅いコピー)”である」 という点です。
シャローコピー(浅いコピー)とは何か
1階層目だけコピーされる
シャローコピーは、
「オブジェクトの“1階層目”だけをコピーする」 コピーです。
次の例を見てください。
const original = {
name: "太郎",
details: {
age: 25,
city: "Tokyo",
},
};
const copy = { ...original };
copy.name = "花子";
copy.details.age = 30;
console.log(original.name); // 太郎(変わらない)
console.log(original.details.age); // 30(変わってしまう)
JavaScriptname を変えたときは、original と copy は別々なので、元は変わりません。
しかし details.age を変えたときは、original.details と copy.details が「同じオブジェクト」を指しているため、
片方を変えるともう片方も変わります。 Zenn
ここがシャローコピーの本質です。
- 1階層目のプロパティ(
name,detailsという“枠”)はコピーされる - でも、その中に入っているオブジェクトや配列は「参照がコピーされるだけ」
つまり、
「表面だけ別物、中身のネストされたオブジェクトは共有」
という状態になります。
スプレッド構文と Object.assign のイメージ
スプレッド構文 { ...obj }
スプレッド構文は、
「オブジェクトの中身を“広げて”新しいオブジェクトに詰め直す」 イメージです。
const original = { name: "太郎", age: 25 };
const copy = { ...original };
console.log(copy); // { name: "太郎", age: 25 }
JavaScriptさらに、コピーしながら上書きもできます。
const original = { name: "太郎", age: 25 };
const copy = { ...original, age: 30 };
console.log(copy); // { name: "太郎", age: 30 }
console.log(original); // { name: "太郎", age: 25 }
JavaScriptObject.assign
Object.assign も、
「プロパティを別のオブジェクトにコピーする」ためのメソッドです。
const original = { name: "太郎", age: 25 };
const copy = Object.assign({}, original);
console.log(copy); // { name: "太郎", age: 25 }
JavaScript第一引数 {} が「コピー先」、
第二引数以降が「コピー元」です。
const copy = Object.assign({}, original, { age: 30 });
JavaScriptと書けば、
「コピーしつつ age を上書き」ということもできます。
基礎としては、
「どちらも“シャローコピー”で、書き味の違いだけ」
くらいの理解で十分です。
「完全に独立させたい」ならディープコピーが必要になる
ディープコピー(深いコピー)とは
シャローコピーは「1階層目だけ別物」でした。
それに対して ディープコピー(深いコピー) は、
「ネストされたオブジェクトや配列も含めて、全部まるごと別物にするコピー」 です。
イメージとしては、
- シャローコピー:表面だけ別の箱、中の箱は共有
- ディープコピー:中の箱も、その中の箱も、全部新しく作り直す
という違いです。
初心者の段階では、
「ネストされたオブジェクトを含むデータを完全に独立させたいときは、
シャローコピーでは足りないことがある」
という感覚だけ持っておけば十分です。
初心者として「オブジェクトのコピー」で本当に押さえてほしいこと
一番大事なのは、この3つです。
1つ目:
「const b = a; はコピーではなく“同じものへの別名”」
片方を変えるともう片方も変わる。
2つ目:
「{ ...obj } や Object.assign({}, obj) で“別のオブジェクト”を作れる」
ただし、それはシャローコピーであり、
ネストされたオブジェクトは共有される。
3つ目:
「ネストまで完全に独立させたいときは“ディープコピー”が必要になる」
(方法はいろいろあるが、まずは「そういう問題がある」と知っておく)
まずは、こんなコードを自分で打ってみてください。
const original = {
name: "太郎",
details: {
age: 25,
},
};
const copy = { ...original };
copy.name = "花子";
copy.details.age = 30;
console.log("original:", original);
console.log("copy:", copy);
JavaScriptそして出力を見ながら、
「どこが独立していて、どこがつながっているのか」
を自分の目で確かめてみる。
その体験が、
「オブジェクトは“値”ではなく“参照”で扱われる」
という感覚を、しっかり身体に刻んでくれます。
