Code Fix

中級

スプレッド構文{ ...obj }はネストした値までは複製しない(浅いコピー)

{ ...obj }や[...arr]は最上位のプロパティだけをコピーします。値がオブジェクトや配列の場合、コピー先とコピー元は同じ参照を共有したままです。

なぜエラーが出ないのか

出力: Osaka
(エラーなし)
JavaScriptは何も報告しません。文法として正しいためです
出力: Osaka
実際の挙動 — 期待した結果と食い違っている箇所
見つけ方
エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます

このエラーが出る典型パターン

パターン1

 1  const original = { name: "Al", address: { city: "Tokyo" } };
 2  const copy = { ...original }          ;
                             ^
 3  copy.address.city = "Osaka";
 4  console.log(original.address.city);
出力: Osaka期待: Tokyo (エラーにはなりません)

スプレッド構文は最上位のプロパティしかコピーしません。addressオブジェクトは元と同じ参照のままなので、コピー先を変更すると元のデータも変わります。structuredClone()は深いコピーになります。

直し方: { ...original } を structuredClone(original) にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  const users = [{ name: "Al" }];
 2  const copy = [...users]            ;
                            ^
 3  copy[0].name = "Bo";
 4  console.log(users[0].name);
出力: Bo期待: Al (エラーにはなりません)

配列のスプレッドも要素自体(オブジェクト)はコピーせず参照をコピーします。要素の中身まで独立させたい場合はstructuredClone()を使います。

直し方: [...users] を structuredClone(users) にします。

この問題を解いてみる →

パターン3

 1  const state = { tags: ["a", "b"] };
 2  const copy = { ...state }          ;
                            ^
 3  copy.tags.push("c");
 4  console.log(state.tags.length);
出力: 3期待: 2 (エラーにはなりません)

tagsプロパティは配列という参照型なので、スプレッドでコピーしても同じ配列を共有したままです。コピー先へのpushが元のstateにも反映されてしまいます。

直し方: { ...state } を structuredClone(state) にします。

この問題を解いてみる →

パターン4

 1  const profile = { name: "Al", hobbies: ["chess"] };
 2  const copy = { ...profile }              ;
                               ^
 3  copy.hobbies.push("golf");
 4  console.log(profile.hobbies.length);
出力: 2期待: 1 (エラーにはなりません)

hobbies配列は参照型なので、スプレッドでコピーしても同じ配列を共有したままです。

直し方: { ...profile } を structuredClone(profile) にします。

この問題を解いてみる →

パターン5

 1  const cart = { items: [{ name: "apple" }] };
 2  const copy = { ...cart }               ;
                              ^
 3  copy.items[0].name = "banana";
 4  console.log(cart.items[0].name);
出力: banana期待: apple (エラーにはなりません)

配列の中のオブジェクトまでは複製されません。要素自体を書き換えると元のデータにも影響します。

直し方: { ...cart } を structuredClone(cart) にします。

この問題を解いてみる →

よくある誤解

「スプレッド構文でコピーしたから元のデータは安全」とは限りません。ネストしたオブジェクト・配列は参照がそのままコピーされるため、コピー先を変更すると元のデータも変わってしまいます。

実務での勘所

ネストしたデータまで完全に複製したい場合、以前はJSON.parse(JSON.stringify(obj))という変換を経由する裏技がよく使われていましたが、関数やundefined、Dateオブジェクトなどが正しく複製できないという制約がありました。現在はstructuredCloneというグローバル関数が標準で使えるようになっており(Node.js 17以降、主要ブラウザ)、循環参照やDate、Mapなど多くの型を正しく再帰的に複製できます。ネストしたオブジェクトを本当に独立させたい場面では、まずこの関数を検討する価値があります。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)