順番に読み進めながら学べます

スプレッド構文 — コピー・結合・設定の上書き

配列やオブジェクトの中身をその場に並べるスプレッド構文で、元を変えずにコピー・結合し、既定の設定にユーザーの変更だけを重ねる書き方を扱います。

在庫ありの商品リストと入荷待ちの商品リストを 1 本にまとめたい、既定の設定にユーザーが変更した項目だけを重ねたい。こうした場面では、元のデータをそのまま残したまま、中身を並べた新しい配列やオブジェクトを作ります。

この記事では、そのためのスプレッド構文と、同じキーが重なったときにどちらが残るかを扱います。

中身をその場に並べる — スプレッド構文

在庫ありの商品リストと入荷待ちの商品リストを 1 本にまとめたいとき、push で 1 件ずつ足していくと元の配列が書き換わります。元のリストはそのまま残したまま、中身を並べた新しい配列を作りたい場面です。

スプレッド構文... を値の前に付けて、配列やオブジェクトの中身をその場に並べる書き方)を使うと、const allItems = [...inStock, ...backOrder]; で結合した新しい配列を作れます。[...inStock] のように 1 つだけ書けば、同じ中身の別の配列を作るコピーです。

オブジェクトでも { ...product } の形で同じように書けます。ただしこれは1 段目だけを写すコピーで、内側にオブジェクトや配列を持つ場合の挙動は、参照とコピーの記事で扱います。

... は書く場所で役割が変わります。= の左側に書けば残りをまとめる rest、右側の []{} の中に書けば中身を並べるスプレッドです。記号は同じですが、まとめる側と広げる側で向きが逆です。

... は中身をその場に並べる
[...inStock]中身をそのまま並べる同じ中身の別の配列[...inStock,...backOrder]書いた順に並べる結合された配列["新着",...inStock]先頭に1 件足す1 件多い配列
並べた順にそのまま新しい配列へ入ります。元の配列は書き換わりません
const inStock = ["ペン", "テープ"];
const backOrder = ["ボード"];

// 2 つを並べた新しい配列を作る
const allItems = [...inStock, ...backOrder];
console.log(allItems.join(", "));   // ペン, テープ, ボード

// 元の配列はそのまま
console.log(inStock.length);        // 2

// 途中に個別の値を混ぜて書ける
const featured = ["今週のおすすめ", ...inStock];
console.log(featured.length);       // 3

// オブジェクトも同じ書き方でコピーできる
const product = { name: "テープ", price: 380 };
const copied = { ...product };
console.log(copied.price);          // 380

在庫ありと入荷待ちの 2 本の配列から、表示用の配列を組み立てます。inStockbackOrder は宣言済みです。

① 2 つを結合した新しい配列を作り、「, 」でつないで表示してください。

② 結合した配列の件数を表示してください。

③ 結合した配列の先頭に「新着」を加えた配列を作り、先頭 2 件を「, 」でつないで表示してください。

④ 在庫ありの配列をコピーした新しい配列を作り、その件数を表示してください。

(正しく実行できれば解説が表示されます)

JavaScript / TypeScript エディタ

コードを実行してください

コピーした配列やオブジェクトを書き換えて、元がどうなるかを確かめます。inStockproduct は宣言済みです。

inStock をコピーした配列を作り、そこに「のり」を足してから、その件数を表示してください。

② 元の inStock の件数を表示してください。

product をコピーしたオブジェクトを作り、その価格を 480 に書き換えて表示してください。

④ 元の product の価格を表示してください。

JavaScript / TypeScript エディタ

コードを実行してください

設定を重ねる — あとに書いたほうが勝つ

アプリの設定は、既定値をひととおり用意しておき、ユーザーが変更した項目だけを上書きする形にすることがあります。変更された項目だけを 1 つずつ調べて代入すると、項目が増えるたびにコードを足す必要があります。

オブジェクトのスプレッド構文を 2 つ並べて { ...defaultSettings, ...userSettings } と書くと、同じキーはあとに書いたほうの値で上書きされます。ユーザーが指定していないキーは既定値のまま残ります。

書く順序がそのまま優先順位になるので、既定値を先に、優先したい値をあとに置きます。

重ねると、あとに書いたほうが残る
既定の themelightあとに darkが来るdark既定の件数20あとに 50が来る50既定の通知trueあとの側に同じキーが無いtrue のまま
同じキーは あとに書いたオブジェクトの値 に上書きされ、片方にしか無いキーはそのまま残ります。
const defaultSettings = { theme: "light", itemsPerPage: 20, notifications: true };
const userSettings = { theme: "dark" };

const merged = { ...defaultSettings, ...userSettings };
console.log(merged.theme);           // dark(あとに書いたほうが勝つ)
console.log(merged.itemsPerPage);    // 20(ユーザー側に無いので既定値のまま)

// 順序を逆にすると既定値が勝ってしまう
const reversed = { ...userSettings, ...defaultSettings };
console.log(reversed.theme);         // light

// あとの側の値が undefined でも、キーがあれば上書きされる
const partial = { theme: undefined };
const broken = { ...defaultSettings, ...partial };
console.log(broken.theme);           // undefined

undefined でも上書きされる

重ねる側にキーがあれば、値が undefined でも上書きが起こります。フォームから受け取った未入力の項目をそのまま重ねると、既定値が消えて undefined に置き換わります。値が入っているときだけ使いたい項目は、userSettings.theme ?? defaultSettings.theme のように 1 項目ずつ判定します。

既定の設定にユーザーが変更した項目を重ねて、実際に使う設定を組み立てます。defaultSettingsuserSettings は宣言済みです。

① 既定の設定にユーザーの設定を重ねたオブジェクトを作り、そのまま表示してください。

② 重ねた結果のテーマを表示してください。

③ 重ねる順序を逆にしたオブジェクトを作り、そのテーマを表示してください。

④ ユーザー側に無い通知設定が、重ねた結果どうなっているかを表示してください。

JavaScript / TypeScript エディタ

コードを実行してください
QUIZ

理解度チェック

まずは1問ずつ答えてみましょう。

Q1const copied = [...items]; のあとに copied.push("X") すると、元の items はどうなりますか?

Q2const merged = { ...defaults, ...user }; で、両方にある同じキーの値はどうなりますか?

Q3重ねる側のオブジェクトにキーがあり、その値が undefined だったとき、既定値はどうなりますか?