オブジェクトのクローン(浅)— スプレッド構文 {…obj} の基本と実践
浅いクローンは「一番上の層だけ」をコピーして、新しいオブジェクトを作る方法です。最短はスプレッド構文 {...obj}。ネストされたオブジェクトや配列は参照が共有される点だけ、しっかり掴んでおきましょう。
基本の使い方
const original = { name: "Aki", age: 22 };
const copy = { ...original };
copy.age = 23;
console.log(original.age); // 22(上位のプロパティは独立)
JavaScript- スプレッド構文:
{...obj}で自身の列挙可能な「文字列キー」プロパティを上位層だけコピー。 - ポイント: ネスト内(オブジェクト・配列など)は「同じ参照」を持つため、内側を変更すると元にも影響します。
ネストの落とし穴(参照が共有される)
const original = { profile: { city: "Tokyo" }, tags: ["js","web"] };
const copy = { ...original };
// 内側を変更すると両方に反映される
copy.profile.city = "Osaka";
copy.tags.push("frontend");
console.log(original.profile.city); // "Osaka"
console.log(original.tags); // ["js","web","frontend"]
JavaScript- 理由: 浅いクローンは「上位のキー→参照」をコピー。参照先の中身は別クローンされない。
すぐ使えるテンプレート集
部分的に深くコピーする(ピンポイント対応)
const original = { user: { name: "Aki", addr: { city: "Tokyo" } } };
// userまでは浅コピー、addrは手動で浅コピーして“段階的に”参照切り離し
const copy = {
...original,
user: {
...original.user,
addr: { ...original.user.addr },
},
};
copy.user.addr.city = "Osaka";
console.log(original.user.addr.city); // "Tokyo"(ここは分離できた)
JavaScript- ポイント: 必要な深さまで段階的に
{...}を噛ませて参照を切る。
配列を含むオブジェクトを浅クローン
const original = { list: [1,2,3] };
const copy = { ...original, list: [...original.list] }; // 配列は別途スプレッド
copy.list[0] = 9;
console.log(original.list[0]); // 1
JavaScript- ポイント: 配列の分離は
[...]。オブジェクトの分離は{...}。
Object.assign でも同じ(浅コピー)
const original = { a: 1, b: { c: 2 } };
const copy = Object.assign({}, original);
copy.b.c = 3;
console.log(original.b.c); // 3(ネストは共有)
JavaScript- ポイント: スプレッドと assign は同じ“浅い”挙動。お好みで。
よくある型別の注意点
- 関数・Date・Map/Set: 浅コピーでは「参照がそのまま」。Dateは同じインスタンス、Map/Setも同じコレクションを指す。
- Symbolキー: スプレッドは列挙可能な Symbol もコピーされますが、存在や列挙設定に注意。
- 非列挙プロパティ:
enumerable: falseなプロパティはスプレッドではコピーされません(必要なら defineProperty で手動移植)。
深いクローンが必要なとき(参考)
- 簡易:
JSON.parse(JSON.stringify(obj))- 注意: 関数/undefined/Symbol/Date/Map/Set/BigIntなどは失われる、循環参照で失敗。
- 推奨:
structuredClone(obj)(ブラウザ/Nodeの新しめ環境)- 特徴: 多くの型に対応し、循環参照もOK。Map/Set/Date/ArrayBufferなどを安全に複製。
- ライブラリ: ルール込みの深いクローンが必要なら専用ライブラリを検討。
実務での使い分け指針
- UI状態の更新(部分的に参照を切る): 必要な階層にだけ
{...}や[...]を差し込む。 - データ加工のワンショット: 浅コピーで十分なら
{...obj}。ネストの変更が元を汚さないよう、適宜“段階的スプレッド”。 - 厳密な複製が必要:
structuredClone。JSON法は「純粋なデータのみ」の場面に限定。
練習問題(手を動かして覚える)
- 1. 浅クローンでネストが共有されることを確認
const o = { inner: { x: 1 } };
const c = { ...o };
c.inner.x = 9;
console.log(o.inner.x); // 9
JavaScript- 2. 段階的スプレッドで参照を切る
const o = { a: { b: { c: 1 } } };
const c = { ...o, a: { ...o.a, b: { ...o.a.b } } };
c.a.b.c = 7;
console.log(o.a.b.c); // 1
JavaScript- 3. 配列を別コピーして安全に編集
const o = { arr: [1,2,3] };
const c = { ...o, arr: [...o.arr] };
c.arr.push(4);
console.log(o.arr.length); // 3
JavaScript直感的な指針
- 上位だけ複製、内側は共有: 浅クローンの本質。
- 必要な階層だけ分離: 段階的に
{...}/[...]を入れる。 - 深い複製が必要なら:
structuredCloneを優先。JSON法は型が失われるので用途限定。
