スプレッド構文{ ...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) にします。
よくある誤解
「スプレッド構文でコピーしたから元のデータは安全」とは限りません。ネストしたオブジェクト・配列は参照がそのままコピーされるため、コピー先を変更すると元のデータも変わってしまいます。
まとめ
スプレッド構文{ ...obj }はネストした値までは複製しない(浅いコピー)は中級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。