スプレッド構文
このレッスンで分かること
...は配列やオブジェクトの中身をその場にばらまく記号です- オブジェクトを合成すると、後に書いたキーが勝ちます
渡しただけなのに、呼び出し元の配列が増えている
前のレッスンで確かめたとおり、push は受け取った配列そのものを書き換えます。関数の中で push すると、呼び出した側の配列まで一緒に変わります。
JavaScript
const original = ["HTML", "CSS"];
const updated = original;
updated.push("JavaScript");
console.log(original.length); // 3 元も増えている呼び出し側から見れば「渡しただけなのに壊された」わけです。こうした書き換えは、規模が大きくなるほど原因の特定が難しいバグになります。分割代入が値を取り出す道具だったのに対して、スプレッド構文は中身をばらまいて新しく作り直す道具です。
JavaScript
const skills = ["HTML", "CSS"];
console.log([...skills, "JavaScript"]); // ["HTML", "CSS", "JavaScript"]
console.log(skills.length); // 2 元はそのまま...skills は「skills の中身をこの場にばらまく」という意味です。新しい [] の中でばらまいて後ろに 1 つ置けば、元を触らずに要素が 1 つ増えた別の配列ができあがります。
| やりたいこと | 書き方 | 元の配列 |
|---|---|---|
| コピー | [...arr] | 変わらない |
| 末尾に追加 | [...arr, x] | 変わらない |
| 先頭に追加 | [x, ...arr] | 変わらない |
| 末尾に追加 (破壊的) | arr.push(x) | 変わる |
元のデータを書き換えず、新しいデータを作る書き方をイミュータブルな更新と呼びます。React をはじめとする現代のフレームワークは、この前提で変化を検知しています。
{} の中では、後に書いたほうが勝つ
オブジェクトでも同じ記号が使えます。こちらはキーが重なったときの挙動が要点です。
JavaScript
const defaults = { theme: "light", fontSize: 16 };
const saved = { theme: "dark" };
console.log({ ...defaults, ...saved });
// { theme: "dark", fontSize: 16 }同じキーがあれば、後に書いたほうが勝ちます。この性質を使うと「既定値を先に置き、実際の値で上書きする」という定石が書けます。順番を逆にすると既定値のほうが勝ってしまうので、並べる順序はとても重要です。{ ...defaults, fontSize: 20 } のように、1 つのキーだけ差し替えた新しいオブジェクトを作るのも同じ形です。
コピーは一段だけ
ここがこのレッスンでいちばん大事なところです。... で作られるコピーは浅いコピーです。
JavaScript
const config = { theme: "dark", editor: { tabSize: 2 } };
const copy = { ...config };
copy.theme = "light";
console.log(config.theme); // dark こちらは無事
copy.editor.tabSize = 4;
console.log(config.editor.tabSize); // 4 こちらは道連れ1 階層目は新しく作られますが、値がオブジェクトや配列なら、その実体は元と共有されたままです。前のレッスンの「同じ場所を指す 2 枚の札」がそのまま残っている状態です。深いところまで分けたいなら、階層ごとに重ねます。
JavaScript
const deep = { ...config, editor: { ...config.editor } };よくある間違い
- 合成の順番を逆にする — 既定値を後ろに置くと、実際の値が既定値で上書きされます。既定値は必ず先に置きます
- オブジェクトを
[]で展開する —[...config]はconfig is not iterableで止まります。[]の中で展開できるのは配列や文字列などで、オブジェクトを展開するなら{}の中で行います
要件
- 関数名は
withNewWork、引数はworksとnewWorkの 2 つ { views: 0, published: false }を既定値としてnewWorkとマージすること- 戻り値は元の配列に手を加えずに作った新しい配列
pushを使わずスプレッド構文で書くこと
入出力例
withNewWork([], {"title":"プロフィールサイト","year":2026}) → [{"published":false,"title":"プロフィールサイト","views":0,"year":2026}]
withNewWork([{"published":true,"title":"プロフィールサイト","views":120,"year":2026}], {"title":"自己紹介カード","views":80,"year":2026}) → [{"published":true,"title":"プロフィールサイト","views":120,"year":2026},{"published":false,"title":"自己紹介カード","views":80,"year":2026}]
withNewWork([{"published":true,"title":"プロフィールサイト","views":120,"year":2026},{"published":false,"title":"自己紹介カード","views":80,"year":2026}], {"published":true,"title":"スキル一覧の表","year":2026}) → [{"published":true,"title":"プロフィールサイト","views":120,"year":2026},{"published":false,"title":"自己紹介カード","views":80,"year":2026},{"published":true,"title":"スキル一覧の表","views":0,"year":2026}]
withNewWork([], {}) → [{"published":false,"views":0}]