JavaScript 逆引き集 | オブジェクトのマージ

JavaScript JavaScript
スポンサーリンク

オブジェクトのマージ(浅)— スプレッド構文 {…a, …b} の基本と実践

複数の設定やデータを“上書きルール”で一つにまとめたいときに使うのがスプレッド構文のマージ。右側が勝つ(後勝ち)という直感的な挙動で、初心者でも扱いやすいです。


基本の使い方と後勝ちルール

const a = { theme: "light", pageSize: 20 };
const b = { pageSize: 50, compact: true };

const merged = { ...a, ...b };
console.log(merged);
// { theme: "light", pageSize: 50, compact: true }
JavaScript
  • 後勝ち: 同じキーがある場合、右側(後ろ)の値で上書きされます。
  • 新規キー: 片方にしかないキーはそのまま取り込まれます。
  • 浅いマージ: 上位階層だけコピー。ネストは「参照ごと」上書きされます。

よく使うテンプレート集

1) デフォルト設定 + ユーザー設定(右側が優先)

const defaults = { theme: "light", pageSize: 20, compact: false };
const user = { pageSize: 50 };

const config = { ...defaults, ...user };
// { theme: "light", pageSize: 50, compact: false }
JavaScript

2) ネストは丸ごと上書き(部分更新ではない)

const base = { ui: { theme: "light", fontSize: 14 } };
const patch = { ui: { fontSize: 16 } };

const merged = { ...base, ...patch };
// { ui: { fontSize: 16 } }(themeは消える=ネストは“入れ替え”)
JavaScript

3) 部分的にネストを保ちながら更新(段階的スプレッド)

const base = { ui: { theme: "light", font: { size: 14, family: "Noto" } } };
const patch = { ui: { font: { size: 16 } } };

// uiまでは再構築し、fontはさらに深くマージ
const merged = {
  ...base,
  ui: {
    ...base.ui,
    font: { ...base.ui.font, ...patch.ui.font },
  },
};
// { ui: { theme: "light", font: { size: 16, family: "Noto" } } }
JavaScript

4) 配列は“結合”ではなく“上書き”

const a = { tags: ["js", "web"] };
const b = { tags: ["ui"] };

const merged = { ...a, ...b };
// { tags: ["ui"] }(配列は結合されない=右で上書き)
JavaScript
  • 結合したいなら tags: [...a.tags, ...b.tags] のように明示します。

5) 条件付きマージ(存在するものだけ)

const base = { a: 1, b: 2 };
const maybe = null;

const merged = { ...base, ...(maybe ?? {}) };
// null/undefined のときは無視、オブジェクトなら展開
JavaScript

Object.assign との違いと使い分け

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

// 新しいオブジェクトに集約(不変に優しい)
const m1 = { ...a, ...b };

// 既存オブジェクトへ代入(破壊的)
const target = {};
Object.assign(target, a, b); // target が更新される
JavaScript
  • どちらも浅いマージ: ネストは参照単位で上書き。
  • 不変を保つなら: スプレッドで「新しいオブジェクト」を作るのが定石。
  • 複数をまとめる: assignもスプレッドも任意の数のオブジェクトに使えます。

実務での便利パターン

安全な更新(状態管理に向く書き方)

// React/Vueなどの状態更新で
const next = { ...state, loading: true, error: null };
JavaScript

キーの追加・上書きを同時に

const user = { name: "Aki", role: "user" };
const patched = { ...user, role: "admin", lastLogin: Date.now() };
JavaScript

許容キーのみをマージ(ホワイトリスト)

const payload = { id: 1, name: "Aki", debug: true };
const allowed = (({ id, name }) => ({ id, name }))(payload);

const merged = { ...defaults, ...allowed };
JavaScript

マージ結果を整形(配列だけ結合)

const a = { tags: ["js"], meta: { v: 1 } };
const b = { tags: ["web"], meta: { v: 2 } };

const merged = {
  ...a, ...b,
  tags: [...(a.tags ?? []), ...(b.tags ?? [])], // 結合
};
JavaScript

落とし穴と対策

  • ネストが消える問題: 同じキーにオブジェクトが来ると“丸ごと上書き”される。
    対策: 段階的に {...} を入れて必要な深さまで手でマージする。
  • 配列は結合されない: 期待に反して右で上書き。
    対策: [...] で明示的に結合する。
  • 非列挙・Symbolキー: スプレッドは「列挙可能な自前プロパティ」を対象。非列挙は含まれない。
    対策: 特殊なプロパティは Object.getOwnPropertyDescriptors などで扱う。
  • getter/setterの挙動: スプレッドは「値」をコピー。プロパティディスクリプタ(getter/setter)は再現されない。
    対策: ディスクリプタごと複製したい場合は Object.defineProperties 系で移植。
  • null/undefinedの展開: ...null...undefined はエラー。
    対策: ...(obj ?? {}) のようにガード。
  • 深いマージが必要: ルール付きでオブジェクトを“結合”したいとき、スプレッドだけでは冗長。
    対策: 仕様を関数化するか、必要に応じて専用の“深いマージ”手法(自作・ライブラリ)を検討。

練習問題(手を動かして覚える)

// 1) 後勝ちを確認
const a = { level: 1, hp: 100 };
const b = { hp: 80, mp: 50 };
console.log({ ...a, ...b }); // { level:1, hp:80, mp:50 }

// 2) ネストの部分更新(段階的スプレッド)
const base = { user: { name: "Aki", pref: { lang: "ja", tz: "Asia/Tokyo" } } };
const patch = { user: { pref: { lang: "en" } } };
const merged = {
  ...base,
  user: { ...base.user, pref: { ...base.user.pref, ...patch.user.pref } },
};
console.log(merged.user.pref); // { lang:"en", tz:"Asia/Tokyo" }

// 3) 配列は結合したい
const p = { tags: ["a", "b"] };
const q = { tags: ["c"] };
console.log({ ...p, ...q, tags: [...p.tags, ...q.tags] }); // ["a","b","c"]

// 4) ガード付きの条件マージ
const base2 = { a: 1 };
const optional = Math.random() > 0.5 ? { b: 2 } : null;
const merged2 = { ...base2, ...(optional ?? {}) };
console.log(merged2);
JavaScript

直感的な指針

  • 浅いマージの定石: {...left, ...right}(右が勝つ)。
  • ネストを維持した更新: 必要な深さまで段階的に {...} を噛ませる。
  • 配列は上書きされる: 結合したいなら [...] を明示。
  • 不変更新を習慣化: 新しいオブジェクトを作って状態を安全に更新。
  • 特殊プロパティや深い結合が必要なら: 仕様を関数化、または専用の深いマージ手段を選ぶ。
タイトルとURLをコピーしました