JavaScript | 基礎構文:オブジェクト - スプレッド構文による浅いコピー

JavaScript JavaScript
スポンサーリンク

スプレッド構文による「浅いコピー」って何者?

スプレッド構文 { ...obj } は、
「既存のオブジェクトの中身を“広げて”、新しいオブジェクトを作る書き方」 です。

そしてここで超重要なのが、
このコピーは 「浅いコピー(シャローコピー)」 だということ。

一言でいうと、

表面のプロパティは新しいオブジェクトにコピーされるけど、
中に入っているオブジェクトや配列は“共有される”

というコピーです。


まずは一番シンプルな浅いコピーの例

「別のオブジェクト」がちゃんとできるパターン

const original = {
  name: "太郎",
  age: 25,
};

const copy = { ...original };

copy.name = "花子";

console.log(original.name); // 太郎
console.log(copy.name);     // 花子
JavaScript

ここで起きていることはこうです。

{ ...original } によって、
name: "太郎", age: 25 というプロパティを持つ
新しいオブジェクト が作られます。

そのため、copy.name を書き換えても、
original.name には影響しません。

ここまでは「理想的なコピー」に見えますよね。
このレベルなら、スプレッド構文はとても素直に動きます。


「浅いコピー」が顔を出すのはネストしたとき

ネストされたオブジェクトがある場合

本番はここからです。

const original = {
  name: "太郎",
  details: {
    age: 25,
    city: "Tokyo",
  },
};

const copy = { ...original };

copy.name = "花子";
copy.details.age = 30;

console.log("original.name:", original.name);         // 太郎
console.log("copy.name:", copy.name);                 // 花子

console.log("original.details.age:", original.details.age); // 30
console.log("copy.details.age:", copy.details.age);         // 30
JavaScript

name を変えたときは、
originalcopy は別々なので問題なし。

でも details.age を変えたら、
元の original.details.age まで変わってしまっています。

なぜか?

{ ...original } がやっているのは、

  • name プロパティの値 "太郎" をコピー
  • details プロパティの値(オブジェクトへの参照)をコピー

という動きだからです。

つまり、

copy.detailsoriginal.details
「同じオブジェクトを指している」 状態になっています。

ここが浅いコピーの本質です。
「1階層目の“枠”はコピーされるが、その中身のオブジェクトまではコピーされない」。


図でイメージしてみる

参照が“複製される”だけ

ざっくりイメージすると、こうです。

original ----> { 
  name: "太郎",
  details: ----> { age: 25, city: "Tokyo" }
}

copy ----> {
  name: "太郎",
  details: ----> (↑ original.details と同じもの)
}

details という“矢印”だけがコピーされていて、
矢印の先にあるオブジェクトは共有されています。

だから、copy.details.age を変えると、
同じ矢印の先にいる original.details.age も変わるわけです。


スプレッド構文の基本的な使い方

単純なコピー

const original = { a: 1, b: 2 };
const copy = { ...original };

console.log(copy); // { a: 1, b: 2 }
JavaScript

コピーしながら一部を上書き

const original = { a: 1, b: 2 };
const copy = { ...original, b: 99 };

console.log(original); // { a: 1, b: 2 }
console.log(copy);     // { a: 1, b: 99 }
JavaScript

右側に書いたプロパティが優先されるので、
「元をベースにしつつ、少しだけ変えたオブジェクト」を作るのにとても便利です。

ネストを“1階層だけ”コピーしたいとき

例えば、
details だけは新しいオブジェクトにしたい」
という場合は、こう書けます。

const original = {
  name: "太郎",
  details: {
    age: 25,
    city: "Tokyo",
  },
};

const copy = {
  ...original,
  details: {
    ...original.details,
  },
};

copy.details.age = 30;

console.log(original.details.age); // 25
console.log(copy.details.age);     // 30
JavaScript

ここでは、

  • 外側のオブジェクトを { ...original } でコピー
  • さらに details{ ...original.details } でコピー

という「二重の浅いコピー」をしているので、
details も別物になっています。

ここが重要です。
「浅いコピー」を組み合わせることで、
必要な階層だけ“独立させる”こともできる。


代入との違いをもう一度はっきりさせる

代入は「コピー」ではなく「同じものへの別名」

const original = { name: "太郎" };
const alias = original; // これはコピーではない

alias.name = "花子";

console.log(original.name); // 花子
console.log(alias.name);    // 花子
JavaScript

alias は「original と同じオブジェクトを指す別名」です。

一方、スプレッド構文はこうです。

const original = { name: "太郎" };
const copy = { ...original };

copy.name = "花子";

console.log(original.name); // 太郎
console.log(copy.name);     // 花子
JavaScript

ここでは、
originalcopy は「別のオブジェクト」です。

ここが重要です。
{ ...obj } は“新しいオブジェクトを作る”。
const b = a; は“同じオブジェクトを指す変数を増やす”。
この違いを絶対に混同しないこと。


初心者として「スプレッド構文による浅いコピー」で本当に押さえてほしいこと

大事なポイントをまとめると、こうなります。

1. { ...obj } は「新しいオブジェクト」を作る
代入とは違い、1階層目のプロパティは独立する。

2. これは“浅いコピー”である
ネストされたオブジェクトや配列は「参照がコピーされるだけ」で、共有される。

3. ネストも独立させたいなら、必要な階層ごとに { ...} を重ねる
{ ...obj, nested: { ...obj.nested } } のように書く。

まずは、次のコードを自分の手で打って、
コンソールの結果をじっくり眺めてみてください。

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をコピーしました