スプレッドとレストで組み替える
このレッスンで分かること
- 同じキーがぶつかったら、後ろに書いたほうが残ります
...は置いた場所で、ばらまくか、まとめるかが変わります
使わない引数まで書かされる
呼び出すたびに変わるかもしれない値が増えてくると、引数を 1 つずつ足す設計が破綻します。
JavaScript
applyTheme("light", 16, 3, false);変えたいのが 3 番目だけでも、前の 2 つを書かされます。しかも false が何を意味するのか、呼び出し行を見ても分かりません。設定はオブジェクト 1 つで受け取り、足りないところを既定値で埋める形にします。
JavaScript
function applyTheme(custom = {}) {
const base = { theme: "light", fontSize: 16, columns: 2 };
return { ...base, ...custom };
}
console.log(applyTheme({ columns: 3 }));
// { theme: "light", fontSize: 16, columns: 3 }{ ...base, ...custom } は「base の中身をばらまき、その上に custom の中身をばらまく」という意味です。同じキーがぶつかったときは後に書いたほうが残ります。だから既定値を先、受け取った設定を後ろに置きます。
順番を逆にすると、いつでも既定値が勝つコードになります。指定したはずの設定が効かないという形で表面化するので、原因の特定に時間がかかります。既定値は先、と型で覚えてください。
|| で埋めると 0 が消える
既定値を埋めるだけなら custom.columns || 2 でもよさそうに見えます。ここに落とし穴があります。
| 書きかた | 0 を渡す | null を渡す | キーを渡さない |
|---|---|---|---|
{ ...base, ...custom } | 0 | null | 既定値 |
custom.columns || 2 | 2 | 2 | 2 |
custom.columns ?? 2 | 0 | 2 | 2 |
利用者が「0 でよい」と指定した意図を、|| は消してしまいます。空文字や false でも同じことが起きます。ばらまいて重ねるやりかたは値の真偽を見ないので、0 も false もそのまま採用されます。
ひとつ注意があります。重ねるやりかたはキーさえあれば、値が undefined でも上書きします。{ columns: undefined } を渡すと既定値は消えて undefined になります。呼び出し側で「指定しない」を表したいなら、そのキーを渡さないのが正解です。
残りをまとめる ...
... を値を作る側に書くとばらまき、受け取る側に書くとまとめるという逆向きの働きになります。後者をレストと呼びます。
JavaScript
function joinLabels(separator, ...items) {
return items.join(separator);
}
console.log(joinLabels(" / ", "HTML", "CSS")); // HTML / CSS
console.log(joinLabels(" / ")); // 空文字...items は「残りの引数を全部この配列に入れる」という指示です。1 つも渡されなければ空の配列になるので、undefined かどうかを調べなくても length や map をそのまま呼べます。決まりは 2 つで、必ず最後の引数であることと、1 つしか置けないことです。残りをまとめる、という意味を考えれば当然の制限です。
呼び出す側で配列をばらして渡したいときも ... を書きます。
JavaScript
const chosen = ["HTML", "CSS"];
console.log(joinLabels(" / ", ...chosen)); // HTML / CSS
console.log(joinLabels(" / ", chosen)); // HTML,CSS 配列 1 つとして渡っている下の行は items が [["HTML", "CSS"]] になっています。入れ子が 1 段深いことに気付かないと、結合の結果がおかしいという形で表面化します。
よくある間違い
- 既定値を後ろに置く —
{ ...custom, ...base }と書くと、指定した設定が既定値で上書きされて消えます - レスト引数を前に置く —
function ng(...items, custom) {}は構文エラーです。残りをまとめる以上、後ろに何かを置くことはできません - 浅いコピーのまま中身を書き換える — 重ねて作ったオブジェクトの中の配列は、元の配列と同じものを指しています。
pushすると元まで増えるので、新しい配列を作って差し替えます
要件
- 関数名は buildViewOptions。第1引数は設定オブジェクトで省略可能、第2引数以降はレストで追加タグを受け取る
- 既定値は
{ sort: "year", order: "desc", limit: 5, tags: [] }とし、受け取った設定で上書きする - tags は合成後のタグ配列の後ろに、レストで受け取った追加タグをつないだ新しい配列にする
- 戻り値は sort, order, limit, tags を持つ新しいオブジェクト。limit に 0 を渡したときは 0 のままにする
入出力例
buildViewOptions() → {"limit":5,"order":"desc","sort":"year","tags":[]}
buildViewOptions({"order":"asc"}) → {"limit":5,"order":"asc","sort":"year","tags":[]}
buildViewOptions({"sort":"views","tags":["HTML"]}, "CSS", "JavaScript") → {"limit":5,"order":"desc","sort":"views","tags":["HTML","CSS","JavaScript"]}
buildViewOptions({}, "HTML") → {"limit":5,"order":"desc","sort":"year","tags":["HTML"]}
buildViewOptions({"limit":0}) → {"limit":0,"order":"desc","sort":"year","tags":[]}