JavaScript | 配列・オブジェクト:オブジェクト操作 – Object.assign

JavaScript JavaScript
スポンサーリンク

Object.assign とは何か

Object.assign は「右側のオブジェクトの“自前の列挙可能なプロパティ”を左側(ターゲット)へコピーして上書きする」関数です。ここが重要です:コピーされるのは“文字列キーと Symbol キーのうち enumerable: true なもの”で、継承プロパティは対象外。右側の値が左側を“後勝ち”で上書きします。

const target = { a: 1 };
const source = { b: 2, a: 9 };
Object.assign(target, source);
console.log(target); // { a: 9, b: 2 }(右側が後勝ちで上書き)
JavaScript

基本の使い方(上書き・追加・複数の統合)

右側が後勝ち(上書きのルール)

右から左へ順にコピーされ、同じキーは最後に現れた値で上書きされます。これが設定の“上書き”設計の要になります。

const base = { theme: "light", lang: "ja" };
const override1 = { theme: "dark" };
const override2 = { lang: "en" };
const out = Object.assign({}, base, override1, override2);
// { theme:"dark", lang:"en" }(右側が優先)
JavaScript

ターゲットを“直接”変更する

第1引数(ターゲット)は直接書き換えられ、戻り値もそのターゲットです。共有状態では注意が必要です。

const state = { count: 0 };
const result = Object.assign(state, { count: 1 });
console.log(state === result); // true(同じオブジェクトが返る)
JavaScript

シャローコピー(浅いコピー)であることの理解

浅いコピーの挙動(入れ子は“参照共有”)

Object.assign は“1階層分の値”を複製します。入れ子のオブジェクトは参照がそのままコピーされます。

const orig = { user: { id: 1 }, tag: "x" };
const copy = Object.assign({}, orig); // 浅いコピー
copy.user.id = 999;
console.log(orig.user.id); // 999(内側は同じ参照)
JavaScript

入れ子まで独立させたいなら、深いコピー(structuredClone など)を使うか、必要な階層で手動展開します。

const deepUpdated = Object.assign({}, orig, { user: Object.assign({}, orig.user, { id: 2 }) });
JavaScript

コピー対象の範囲(enumerable/own/Symbol)

何がコピーされ、何がされないか

  • 自身が持つ enumerable: true のプロパティが対象。継承プロパティは無視されます。
  • Symbol キーでも enumerable: true ならコピー対象です。
  • non-enumerable(defineProperty で enumerable: false)はコピーされません。
const k = Symbol("secret");
const obj = {};
Object.defineProperty(obj, "hidden", { value: 1, enumerable: false });
obj[k] = 123;
obj.visible = 2;

const out = Object.assign({}, obj);
console.log(out); // { visible: 2, [Symbol(secret)]: 123 }
JavaScript

アクセサ(get/set)とプロパティ属性の注意点

getter は“呼び出されて値がコピーされる”

assign は“値のコピー”をします。プロパティディスクリプタ(get/set や writable など)は保存されません。

const source = {
  get price() { return 100; }
};
const target = Object.assign({}, source);
console.log(target.price); // 100(値としてコピー。getterではなくなる)
JavaScript

属性は引き継がれない

Object.defineProperty で設定した writable/enumerable/configurable はコピー先では既定の属性になります(値だけ移る)。属性ごと複製したい場合は defineProperty を使って自分で定義します。


例外とガード(安全に使う)

ターゲットが null/undefined は TypeError

第1引数が null/undefined だと例外になります。sources が null/undefined の場合は“無視される”仕様です。

// NG: Object.assign(null, { a: 1 }) // TypeError
Object.assign({}, null, { a: 1 }); // { a: 1 }(null は無視)
JavaScript

非破壊で使う基本形(新インスタンスへ)

共有状態・React などでは、ターゲットに {} を渡して“新しいオブジェクト”へまとめるのが安全です。

const merged = Object.assign({}, base, patch);
JavaScript

実務パターン(設定の既定値・差分反映・部分更新)

既定値を適用して上書き

既定値 → ユーザー設定 → さらに一時的な上書きの順で“後勝ち”にします。

const defaults = { timeout: 5000, retry: 2, theme: "light" };
const userCfg = { retry: 5 };
const runtime = { theme: "dark" };
const cfg = Object.assign({}, defaults, userCfg, runtime);
// { timeout:5000, retry:5, theme:"dark" }
JavaScript

差分だけ反映(空オブジェクトに積む)

変更点だけを抽出してターゲットへ適用します。

function applyDiff(target, diff) {
  return Object.assign(target, diff); // 直接反映(破壊的)
}
// 非破壊にしたいなら:
function mergeDiff(base, diff) {
  return Object.assign({}, base, diff);
}
JavaScript

入れ子の部分更新(シャロー+一点深掘り)

浅いコピー+更新したい階層をもう一段 assign します。

const state = { prefs: { theme: "light", lang: "ja" }, user: { name: "A" } };
const updated = Object.assign({}, state, {
  prefs: Object.assign({}, state.prefs, { theme: "dark" })
});
// prefs.theme だけ非破壊更新
JavaScript

配列やクラスインスタンスでの注意(性質の違い)

配列は“インデックスのプロパティ”を上書き

配列にも使えますが、意図が伝わりづらいので基本は配列専用メソッドを使いましょう。

const a = [10, 20];
Object.assign(a, { 1: 99, 2: 77 }); // インデックス1と2を上書き/追加
console.log(a); // [10, 99, 77]
JavaScript

クラスインスタンスへコピー

インスタンスの自前 enumerable プロパティには有効ですが、メソッドやプロトタイプの振る舞いは“コピーされません”。

class User { constructor() { this.name = "A"; } greet() {} }
const u = new User();
const patch = { name: "B", role: "admin" };
Object.assign(u, patch); // プロパティだけ追加/更新(メソッドはそのまま)
JavaScript

Object.assign とスプレッド構文の使い分け

スプレッドは“書きやすい非破壊合成”

多くの場面で { ...a, ...b } の方が短く読みやすいです。属性や getter の扱いは assign と同様に“値コピー”です。

const merged = { ...defaults, ...userCfg, ...runtime };
JavaScript

assign の利点

  • 実行時に動的な数だけ連結できる(Object.assign({}, ...sources))。
  • 参照で同じオブジェクトを返すため、意図的な“破壊的更新”を明示できる。

まとめ

Object.assign は「右側の自前・列挙可能なプロパティを左側へ“浅く”コピーする」ための基本関数です。右側が後勝ちで上書きされ、ターゲットは“直接変更”される点が最重要。入れ子は参照共有になるため、必要な階層で再度 assign(または structuredClone)して非破壊更新を設計する。コピー対象は enumerable な文字列キーと Symbol キーで、getter は値がコピーされ属性は保持されない。ターゲットの null/undefined は例外になること、sources の null/undefined は無視されることを押さえ、設定の既定値適用・差分反映・部分更新で実務的に使いこなしましょう。

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