JavaScript | ES6+ 文法:スプレッド構文 – 結合処理

JavaScript
スポンサーリンク

スプレッド構文による「結合処理」とは何か

スプレッド構文での結合処理は、... を使って「配列やオブジェクトの中身をバラして並べ直し、新しい配列/オブジェクトを作る」書き方です。
ここが重要です:
配列なら [...] の中で ...array を並べて結合し、
オブジェクトなら {...obj} の中で ...object を並べてマージ(結合+上書き)します。

// 配列の結合
const a = [1, 2];
const b = [3, 4];
const mergedArr = [...a, ...b]; // [1, 2, 3, 4]

// オブジェクトの結合
const base = { id: 1, name: "Alice" };
const extra = { name: "Alice A.", admin: true };
const mergedObj = { ...base, ...extra };
// { id: 1, name: "Alice A.", admin: true }
JavaScript

配列結合でのスプレッド構文(可読性と安全性)

concat との違いと、スプレッドの読みやすさ

ES5 までは、配列の結合といえば concat が定番でした。

const a = [1, 2];
const b = [3, 4];

const merged1 = a.concat(b);      // ES5 スタイル
const merged2 = [...a, ...b];     // スプレッド構文
JavaScript

ぱっと見て、「先に a、そのあと b」と明確に読めるのがスプレッドの強みです。
さらに途中に値を挟むときも直感的に書けます。

const merged3 = [...a, 99, ...b]; // [1, 2, 99, 3, 4]
JavaScript

ここが重要です:
「どの配列の中身が、どの順番で並ぶのか」を、[...] の中の並びだけで視覚的に理解できるようになります。

コピーしながら結合する(元配列を壊さない)

スプレッドで結合した結果は「新しい配列」です。ab 自体は壊れません。

const a = [1, 2];
const b = [3, 4];

const merged = [...a, ...b];

a.push(99);

console.log(a);      // [1, 2, 99]
console.log(merged); // [1, 2, 3, 4](結合結果は変わらない)
JavaScript

ここが重要です:
「結合するときは原則 [...] で新しい配列を作る」という習慣を持つと、
「元データがいつのまにか変わっていた」という事故を減らせます。

3つ以上・条件付き結合も自然に書ける

配列を何個重ねても構いませんし、条件によって配列を入れる・入れないを切り替えることも可能です。

const base = [1];
const extra1 = [2, 3];
const extra2 = [4];

const mergedAll = [...base, ...extra1, ...extra2]; // [1, 2, 3, 4]

const useExtra = false;
const mergedMaybe = [
  ...base,
  ...(useExtra ? extra1 : []),
  99
];
console.log(mergedMaybe); // [1, 99](useExtra=false の場合)
JavaScript

「結合」というよりは、「欲しいものを順番どおりに並べて新配列を組み立てる」イメージで捉えると分かりやすくなります。

オブジェクト結合でのスプレッド構文(マージと上書き)

Object.assign からの進化としてのスプレッド

ES5 までは Object.assign を使ってオブジェクトをマージしていました。

const base = { theme: "light", lang: "ja" };
const extra = { lang: "en", debug: true };

const merged1 = Object.assign({}, base, extra); // ES5 スタイル
const merged2 = { ...base, ...extra };          // スプレッド構文
JavaScript

どちらも結果は同じです。

// { theme: "light", lang: "en", debug: true }
JavaScript

ここが重要です:
スプレッドの方が「どれをベースに、どれで上書きするか」が、
{ ...base, ...extra } という並びで視覚的に分かりやすくなります。

順番が「どっちが勝つか」を決める

同じキーがあった場合、「あとに書いたもの」が上書きします。

const a = { x: 1, y: 2 };
const b = { y: 99, z: 3 };

const ab = { ...a, ...b }; // { x: 1, y: 99, z: 3 }
const ba = { ...b, ...a }; // { y: 2, z: 3, x: 1 }
JavaScript

ここが重要です:
「デフォルト → 上書きする値」の順に並べる、というルールにしておくと迷いません。

const defaultCfg = { theme: "light", lang: "ja" };
const userCfg    = { lang: "en" };

const cfg = { ...defaultCfg, ...userCfg };
// lang は "en" に上書きされる
JavaScript

元オブジェクトを壊さず「一部だけ変えた新オブジェクト」を作る

設定・状態・レスポンスなどを扱うときに、
「元を残しながら、少しだけ変えたバージョン」を作るのに非常に向いています。

const config = { theme: "light", lang: "ja", debug: false };

// debug だけ true にした新しい config
const newConfig = {
  ...config,
  debug: true
};

console.log(config);    // { theme: "light", lang: "ja", debug: false }
console.log(newConfig); // { theme: "light", lang: "ja", debug: true }
JavaScript

実務でよく使う結合パターン(配列編)

ページング結果の結合(前回+今回)

API でページングされたデータを扱うとき、「前のページの配列」と「新しいページの配列」を結合するのは典型です。

const prev = [1, 2, 3];
const next = [4, 5];

const merged = [...prev, ...next];
console.log(merged); // [1, 2, 3, 4, 5]
JavaScript

元の prev / next を壊さないので、再利用やデバッグがしやすくなります。

オプションリストの先頭・末尾に特別な項目を入れる

例えばセレクトボックス用のカテゴリ一覧に「すべて」などを追加するパターンです。

const categories = ["家電", "本", "食品"];

const options = ["すべて", ...categories, "その他"];
console.log(options);
// ["すべて", "家電", "本", "食品", "その他"]
JavaScript

配列をコピーしてから sort / reverse する(安全な並び替え)

sort()reverse() は破壊的メソッドなので、元配列をそのまま渡すと中身が変わってしまいます。
ここが重要です:結合と同じノリで、[...arr] でコピーしてから並び替えます。

const scores = [72, 88, 95, 64];

// 元を壊さない昇順ソート
const sorted = [...scores].sort((a, b) => a - b);

console.log(scores); // [72, 88, 95, 64]
console.log(sorted); // [64, 72, 88, 95]
JavaScript

実務でよく使う結合パターン(オブジェクト編)

デフォルト設定+ユーザー指定をマージ

関数のオプションや API の引数など、「デフォルトとマージして使う」パターンです。

const defaultOptions = {
  method: "GET",
  timeout: 3000,
  cache: "no-cache"
};

function request(url, options = {}) {
  const opts = {
    ...defaultOptions, // まずデフォルト
    ...options         // 次にユーザー指定で上書き
  };
  console.log(url, opts);
}

request("/api/users", { timeout: 5000, method: "POST" });
JavaScript

どの値がどこから来たのかが明確で、拡張もしやすい書き方です。

機密情報を除いた公開用オブジェクトを作る(除外+結合)

結合というより「結合前の整形」ですが、分割代入+スプレッドの組み合わせで「一部を除いたオブジェクト」を作れます。

const fullUser = {
  id: 1,
  name: "Alice",
  password: "secret",
  token: "xxx"
};

// password, token を除外して残りをまとめる
const { password, token, ...safeUser } = fullUser;

// safeUser をさらに加工して返すこともできる
const publicUser = { ...safeUser, role: "guest" };

console.log(publicUser);
// { id: 1, name: "Alice", role: "guest" }
JavaScript

ネストした状態の一部だけ差し替える

外側の状態オブジェクトはスプレッドで結合・上書きし、
内側のネストも同じパターンで結合、という二段構えがよく使われます。

const state = {
  user: { name: "Alice", age: 20 },
  ui: { theme: "light", lang: "ja" }
};

// ui.theme だけ変更した新状態
const newState = {
  ...state,
  ui: {
    ...state.ui,
    theme: "dark"
  }
};

console.log(state.ui.theme);    // "light"
console.log(newState.ui.theme); // "dark"
JavaScript

よくある落とし穴と注意点(重要ポイントの深掘り)

スプレッドによる結合はとても便利ですが、いくつか意識しておきたい点があります。

一つ目は「浅いコピーである」こと。

配列のスプレッド([...arr])も、オブジェクトのスプレッド({ ...obj })も、
外側の箱は新しくなりますが、中のオブジェクトや配列は共有されます。

const list = [{ id: 1 }, { id: 2 }];
const merged = [...list]; // これはコピーでもあり結合でもある

merged[0].id = 99;

console.log(list[0].id);   // 99(中身のオブジェクトは同じ)
console.log(merged[0].id); // 99
JavaScript

深いところまで完全に分離するには「ディープコピー」が必要ですが、
まずは「スプレッドは浅いコピー」という前提で考えることが大事です。

二つ目は「順番が意味を持つ」こと。

配列では、[...a, ...b][...b, ...a] は当然順番が逆になりますし、
オブジェクトでは「どちらの値が勝つか」が順番次第です。
デフォルトと上書きの順を誤ると、意図した通りにマージされません。

三つ目は「巨大データの結合コスト」。

とても大きな配列やオブジェクトを何度もスプレッドで結合すると、
毎回新しいデータ構造を作るので、メモリや速度に影響することがあります。
初心者のうちは気にしすぎなくて良いですが、「コピーや結合はタダではない」ことだけ覚えておくと良いです。

例題で理解を固める

// 1) 配列結合:前回データ+新規データ
const prev = [1, 2, 3];
const next = [4, 5];
const all = [...prev, ...next];
console.log(all); // [1, 2, 3, 4, 5]

// 2) 配列結合:フィルタ結果に特別な要素を付加
const filtered = [10, 20, 30];
const withTotal = [...filtered, "合計:" + filtered.reduce((a, b) => a + b, 0)];
console.log(withTotal); // [10, 20, 30, "合計:60"]

// 3) オブジェクト結合:デフォルト+ユーザーオプション
const defaults = { theme: "light", fontSize: 14 };
const user = { fontSize: 16 };
const settings = { ...defaults, ...user };
console.log(settings); // { theme: "light", fontSize: 16 }

// 4) オブジェクト結合:ネストした一部だけ上書き
const state = {
  user: { name: "Alice", age: 20 },
  ui: { theme: "light", lang: "ja" }
};
const updated = {
  ...state,
  user: { ...state.user, age: 21 }
};
console.log(updated.user); // { name: "Alice", age: 21 }

// 5) 分割+結合:除外してから公開用に整形
const fullUser = { id: 1, name: "Alice", password: "secret", token: "xxx" };
const { password, token, ...safe } = fullUser;
const publicUser = { ...safe, role: "member" };
console.log(publicUser); // { id: 1, name: "Alice", role: "member" }
JavaScript

まとめ

スプレッド構文での結合処理の核心は、「配列やオブジェクトの中身を、その場でバラして並べ直し、新しいデータ構造として組み立て直す」ことです。
配列では [...] の中に ...arr を並べて結合し、元を壊さずに「順番どおりの新配列」を作る。
オブジェクトでは { ...a, ...b } の順番で「どちらの値が勝つか」をコントロールしつつ、元を壊さずに「一部だけ変えた新オブジェクト」を作る。

浅いコピーであること、順番が意味を持つことを理解しておけば、
初心者でもスプレッド構文を使った結合処理を安心して使いこなせるようになります。

タイトルとURLをコピーしました