JavaScript | ES6+ 文法:スプレッド構文 – 上書き順序

JavaScript
スポンサーリンク

スプレッド構文の「上書き順序」とは何か

スプレッド構文では、... を使って配列・オブジェクトの中身を「左から右へ」順番に展開していきます。
ここが最重要ポイントです:

  • オブジェクトの { ...a, ...b } では、あとに書いた ...b が、前に書いた ...a同じキーを持っていた場合に上書きする
  • 配列の [...a, ...b] では、「上書き」ではなく単に「後ろに続けて並ぶ」だけ(要素を上書きはしない)

つまり、「右側ほど優先度が高い」「後に出てきたものが勝つ」と覚えてください。

オブジェクトの上書き順序(ここを一番しっかり理解する)

基本:右側が左側を上書きする

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

const ab = { ...a, ...b };
console.log(ab); // { x: 1, y: 99, z: 3 }

const ba = { ...b, ...a };
console.log(ba); // { y: 2, z: 3, x: 1 }
JavaScript

同じキー y が両方にありますが、

  • { ...a, ...b }a.y が最初に入り、その後 b.y が上書き → y: 99
  • { ...b, ...a }b.y が最初に入り、その後 a.y が上書き → y: 2

ここが重要です:

「デフォルト → 上書きしたい値」の順に並べるのが定石です。

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 にしたい
const newConfig = {
  ...config,
  debug: true
};

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

もし順序を逆にすると、上書きが効きません。

const wrong = {
  debug: true,
  ...config
};

console.log(wrong);
// { theme: "light", lang: "ja", debug: false } ← debug が false に戻ってしまう
JavaScript

ここが重要です:

「あとに書いたものが勝つ」というルールを意識しながら、並べる順番で優先度を表現すること。

配列でのスプレッドと「順序」

配列では、「上書き」ではなく「順番に並ぶだけ」です。
ただし、ここでも「左から右に並ぶ」ことが意味を持ちます。

単純な結合と順番

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

const ab = [...a, ...b];
console.log(ab); // [1, 2, 3, 4]

const ba = [...b, ...a];
console.log(ba); // [3, 4, 1, 2]
JavaScript

配列はキーではなく「位置」で意味を持つので、
どの配列を前・後ろに置くかで意味が変わることを意識します。

途中に値を挟むときの順番

const base = [1, 2];
const merged = [0, ...base, 99];
console.log(merged); // [0, 1, 2, 99]
JavaScript

「0 → base の要素 → 99」という順に並ぶので、
結局「中身をどの順序で読みたいか」が、そのままコードの順序になります。

デフォルト値とユーザー入力のマージ(重要な実務パターン)

オプションオブジェクトのマージ

ライブラリや関数のオプションでよくやるパターンです。

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

この場合、

  • method: "GET""POST" に上書き
  • timeout:30005000 に上書き
  • cache: "no-cache"(ユーザー指定がないのでそのまま)

という結果になります。

ここが重要です:

「上書きしてほしい側(ユーザー側)を後ろに置く」
これを守っていれば、まず大きく間違えません。

ネストしたオブジェクトでの上書き順序

外側と内側の二段構え

状態オブジェクトなどで、ネストした部分だけを変えたいときも、
スプレッド+上書き順序を意識して組み立てます。

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

// ui.theme だけ "dark" に変えたい
const newState = {
  ...state,           // まず state を展開
  ui: {
    ...state.ui,      // 次に ui を展開(デフォルト)
    theme: "dark"     // さらに theme を上書き
  }
};

console.log(newState.ui);
// { theme: "dark", lang: "ja" }
JavaScript

内側でも「ベース → 上書き」の順番を守っているのがポイントです。

もし順序を間違えると、意図した更新になりません。

const badState = {
  ...state,
  ui: {
    theme: "dark",
    ...state.ui
  }
};

console.log(badState.ui);
// { theme: "light", lang: "ja" } ← state.ui が theme を上書きしてしまう
JavaScript

よくある落とし穴と対策(重要部分の深掘り)

「強い値」を前に書いてしまう

初心者がよくやるミスは、優先度の高い値を先に書いてしまうことです。

const strong = { debug: true };
const base   = { debug: false };

const wrong = { ...strong, ...base };
console.log(wrong.debug); // false(負けてしまう)

const correct = { ...base, ...strong };
console.log(correct.debug); // true(勝つ)
JavaScript

対策はシンプルで、「あとから書いた方が強い」というルールを常に思い出すことです。
「どっちが勝ってほしい?」と自問してから、順番を決めましょう。

複数回マージしているうちに順番を見失う

マージが何段階も重なると、「どこで何を上書きしているか」が見えづらくなります。

const a = { x: 1 };
const b = { x: 2 };
const c = { x: 3 };

const result = { ...a, ...b, ...c }; // 結局 x は 3
JavaScript

対策としては:

  • できるだけ「1回のマージ」にまとめる
  • 「デフォルト → 中間 → 最終」のように、層ごとに変数名を分ける

などで、上書きの流れを見えるようにすることです。

配列を「上書きする」つもりで結合してしまう

配列はそもそも「上書き」ではなく「連結」なので、
オブジェクトと同じ感覚で「右側が勝つ」と思っていると勘違いを生みます。

const a = [1, 2];
const b = [3, 4];
const wrong = [...a, ...b]; // これは a の 1,2 が「消える」わけではない
JavaScript

ここが重要です:

  • オブジェクト:キーが同じなら右側が左側を上書き
  • 配列:単純に後ろに続くだけ(上書きの概念はない)

としっかり分けて考えることです。

例題で理解を固める

// 1) デフォルト+ユーザー設定(ユーザー優先)
const defaultCfg = { theme: "light", lang: "ja", debug: false };
const userCfg    = { lang: "en" };

const cfg = { ...defaultCfg, ...userCfg };
console.log(cfg);
// { theme: "light", lang: "en", debug: false }

// 2) ロールごとの権限マージ(後ろのロールが強い)
const roleUser = { read: true, write: false };
const roleAdmin = { write: true, delete: true };

const mergedRole = { ...roleUser, ...roleAdmin };
console.log(mergedRole);
// { read: true, write: true, delete: true }

// 3) ネストした設定の一部更新
const state = {
  ui: { theme: "light", lang: "ja" },
  data: { page: 1 }
};
const nextState = {
  ...state,
  ui: { ...state.ui, theme: "dark" }
};
console.log(nextState.ui);
// { theme: "dark", lang: "ja" }

// 4) 間違った順序の例(意図した変更が消える)
const base = { timeout: 3000, retry: 1 };
const override = { timeout: 5000 };

const wrongOrder = { ...override, ...base };
console.log(wrongOrder.timeout); // 3000(負けてる)

const correctOrder = { ...base, ...override };
console.log(correctOrder.timeout); // 5000(勝ってる)
JavaScript

まとめ

スプレッド構文の「上書き順序」の核心は、「同じキーの場合、右側が左側を上書きする(後から書いたものが勝つ)」という一点です。
オブジェクトでは { ...default, ...override } の順に並べて「デフォルト → 上書き」の意図を表現し、ネストしたオブジェクトでも同じパターンを内側・外側で繰り返す。
配列は上書きではなく単に連結だ、という違いも押さえる。

このルールを明確に意識できていれば、初心者でもスプレッド構文を使った設定マージや状態更新で「なぜか値が戻る/消える」といった事故を大きく減らすことができます。

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