スプレッド構文とレスト構文

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

このレッスンで分かること

  • スプレッド (展開) 構文[...arr] {...obj} で配列 / オブジェクトを展開
  • レスト構文function(...args) で残り引数をまとめる、const [a, ...rest] = arr で残り要素
  • 見た目は同じ ... だが文脈で意味が変わる ─ 展開 vs 集約
  • シャロー (浅い) コピーなのでネストしたオブジェクトは共有

スプレッド構文とレスト構文 とは

...演算子を使った配列・オブジェクトの展開と、残余引数の使い方を練習しよう。本レッスンでは、スプレッド構文とレスト構文 の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。

スプレッド構文とレスト構文

...(3つのドット)を使う2つの構文を学びましょう。同じ記号ですが、使う場所によって意味が異なります。

スプレッド構文(展開)

配列やオブジェクトの要素を展開します。

JavaScript

const arr1 = [1, 2, 3]; const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5] const obj1 = { a: 1, b: 2 }; const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }

レスト構文(残余)

関数の引数や分割代入で「残りの全て」を受け取ります。

JavaScript

function sum(...numbers) { return numbers.reduce((a, b) => a + b, 0); } sum(1, 2, 3, 4); // 10

この問題では、レスト構文で複数の配列を受け取り、スプレッド構文で結合する関数を実装しましょう。

実務で遭遇するパターン

中級レッスンで学んだことが、実際の業務コードでどう登場するかを整理します。

  • 配列マージ[...a, ...b] で結合、concat の代わり
  • オブジェクトマージ{ ...defaults, ...overrides } で設定オブジェクト合成
  • 関数の可変長引数function log(...args) { console.log(...args) }
  • React の props スプレッド<Comp {...props} />

コードレビューで指摘されがちなポイント

PR を出すとシニアから入りやすい指摘です。先回りで身につけておけばレビューが一発で通ります。

  • 深いマージのつもりがシャロー ─ ネスト下のオブジェクトは共有、深いマージは structuredClone か lodash
  • 配列の concat のほうが速い場面 ─ 大量結合では Array.prototype.concat のほうが効率
  • Object.assign との混在 ─ チームでスタイル統一、{...} を推奨
  • ...args の引数チェック忘れ ─ 可変長引数は数 / 型のバリデーションが要る

パフォーマンス考慮事項

  • スプレッドはシャローコピー ─ 大きい配列 / オブジェクトでメモリ消費に注意
  • ホットパスでは concat のほうが速いことも ─ 配列スプレッドはイテレータ経由
  • スプレッドのコピーコスト ─ React の state 更新で頻繁にコピーするとパフォーマンス問題
  • 深いコピーは structuredClone ─ Date / Map / Set / 循環参照も正しく複製。JSON.parse(JSON.stringify) は関数・undefined・Date が欠落するため深いコピーには不向き
この章のポイント

ここまでの要点 ... は文脈で展開 / 集約。配列 / オブジェクト合成、可変長引数、props スプレッドで活躍。シャローコピーの罠に注意。

よくある質問

Q. スプレッド構文の典型的な使い道は?

A. 配列・オブジェクトのコピー([...arr]、{...obj})、結合([...a, ...b])、関数引数の展開(fn(...args))が代表例です。深いコピーはできない点に注意し、ネスト構造を完全複製したいときは structuredClone(obj) を使ってください。

Q. スプレッドとレストの違いは?

A. 見た目は ... で同じですが、配列を展開するのがスプレッド、引数を集めるのがレスト(残余引数)です。function fn(...args) のように関数定義側で使うとレスト、fn(...arr) のように呼び出し側で使うとスプレッドになります。

Q. オブジェクトを展開するときの上書き優先順位は?

A. { ...a, ...b } と書くと b のキーが a を上書きします。デフォルト値を先に展開し、ユーザー設定を後ろで上書きする { ...defaults, ...userOpts } が定番イディオムです。順序を間違えるとデフォルト値が勝ってしまうので注意してください。

次のレッスン

次は 分割代入 で、分割代入 を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. スプレッド構文とレスト構文 の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. スプレッド構文とレスト構文 とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

入出力例

test-cases.txt

mergeAndCount([1,2,3], [3,4,5]){"merged":[1,2,3,3,4,5],"totalCount":6,"unique":[1,2,3,4,5],"uniqueCount":5} mergeAndCount([1,2], [3,4], [5,6]){"merged":[1,2,3,4,5,6],"totalCount":6,"unique":[1,2,3,4,5,6],"uniqueCount":6} mergeAndCount([1,1,1]){"merged":[1,1,1],"totalCount":3,"unique":[1],"uniqueCount":1}
main.js
main.js
学習モード

メモ

スプレッド構文とレスト構文

⌘S で保存