スプレッド構文
このレッスンで分かること
...は配列やオブジェクトの中身をその場にばらまく記号です- オブジェクトを合成すると、後に書いたキーが勝ちます
渡しただけなのに、呼び出し元の配列が増えている
前のレッスンで確かめたとおり、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で止まります。[]の中で展開できるのは配列や文字列などで、オブジェクトを展開するなら{}の中で行います
演習に進む前に
オブジェクトは後に書いた値が優先され、配列は書いた順に並びます。二種類のスプレッドを別々に確かめます。
JavaScript
const setting = { color: "白", size: 10 };
console.log({ ...setting, size: 20 }); // { color: "白", size: 20 }
const steps = ["下書き", "確認"];
console.log([...steps, "公開"]); // ["下書き", "確認", "公開"]
console.log(steps.length); // 2演習の既定値は変数に用意済みです。二つのスプレッドと戻り値を自分で書いてください。
要件
- 関数名は
withNewWork、引数はworksとnewWorkの 2 つ { views: 0, published: false }を既定値としてnewWorkとマージすること- 戻り値は元の配列に手を加えずに作った新しい配列
pushを使わずスプレッド構文で書くこと