JavaScript | 基礎構文:オブジェクト - オブジェクトのコピー

JavaScript JavaScript
スポンサーリンク

オブジェクトのコピーって何者?

「オブジェクトのコピー」は、
「元のオブジェクトとは独立した“もう一つのオブジェクト”を作ること」 です。

ここで一番ハマりやすい落とし穴が、

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(変わってしまう)
JavaScript

name を変えたときは、
originalcopy は別々なので、元は変わりません。

しかし details.age を変えたときは、
original.detailscopy.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 }
JavaScript

Object.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

そして出力を見ながら、
「どこが独立していて、どこがつながっているのか」
を自分の目で確かめてみる。

その体験が、
「オブジェクトは“値”ではなく“参照”で扱われる」
という感覚を、しっかり身体に刻んでくれます。

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