JavaScript | ES6+ 文法:オブジェクト拡張 – Object.assign

JavaScript JavaScript
スポンサーリンク

Object.assign とは何か

Object.assign は、複数のオブジェクトを「左から右へ」順番にコピーして、1つのオブジェクトにまとめるための ES6 のメソッドです。
一言でいうと、「プロパティを上書きしながらマージするコピー機」です。

基本の形はこうです。

Object.assign(コピー先, コピー元1, コピー元2, ...);
JavaScript

コピー先オブジェクトに、コピー元オブジェクトのプロパティが「上書きしながら」追加されます。
ここが重要です:

  • コピー先そのものが「書き換えられる」
  • 同じキーがあった場合は、「後ろに書いたほう」が勝つ(上書きされる)

一番基本の使い方と「上書きのルール」

単純なマージ(結合)の例

const target = { a: 1 };
const source = { b: 2 };

Object.assign(target, source);

console.log(target); // { a: 1, b: 2 }
console.log(source); // { b: 2 }(source はそのまま)
JavaScript

targetsource のプロパティがコピーされ、target 自体が変化します。
Object.assign の戻り値も target です。

const result = Object.assign(target, source);
console.log(result === target); // true
JavaScript

つまり、Object.assign は「戻り値として返すオブジェクト」と「第1引数のオブジェクト」が同じものになります。

同じキーがあった場合(上書き順序)

const target = { a: 1, x: 10 };
const source1 = { b: 2, x: 20 };
const source2 = { c: 3, x: 30 };

const result = Object.assign(target, source1, source2);

console.log(result); // { a: 1, x: 30, b: 2, c: 3 }
JavaScript

ここが重要です:

  • x は target にも source1 にも source2 にもある
  • 最終的には「一番右の source2 の x = 30」が採用される
  • 後ろに書いたものほど「強い」

「デフォルト設定 → 上書き用の設定」という順番で並べると、意図どおりのマージになりやすいです。


「元を壊さないコピー」としての Object.assign

空オブジェクト {} をコピー先にすれば「新しいオブジェクト」を作れる

Object.assign は第1引数(コピー先)を書き換えますが、コピー先に「空オブジェクト {}」を渡せば、
元のオブジェクトは壊さずに「中身をコピーした新しいオブジェクト」を作れます。

const original = { a: 1, b: 2 };

// 空オブジェクトをコピー先にする
const copy = Object.assign({}, original);

copy.a = 99;

console.log(original); // { a: 1, b: 2 }
console.log(copy);     // { a: 99, b: 2 }
JavaScript

ここが重要です:

  • Object.assign(original, ...) と書くと original 自体が変わる(破壊的)
  • Object.assign({}, original, ...) と書くと「新しいオブジェクト」を返してくれる(非破壊)

現在は { ...original } というスプレッド構文を使うことが多いですが、
考え方は Object.assign({}, original) とほぼ同じです。

複数のオブジェクトをまとめてコピーする

const base = { a: 1 };
const extra = { b: 2 };
const override = { a: 9 };

// base を元にしつつ、extra を足し、最後に override で上書き
const merged = Object.assign({}, base, extra, override);

console.log(merged); // { a: 9, b: 2 }
console.log(base);   // { a: 1 }(元はそのまま)
JavaScript

「浅いコピー」であること(ネストは共有される)

浅いコピーとは何か

Object.assign は「浅いコピー(shallow copy)」です。
これは、「一段目のプロパティだけ」を新しいオブジェクトにコピーし、
その中にあるオブジェクトや配列自体は「同じもの(参照)」を共有する、という意味です。

const original = {
  a: 1,
  nested: { x: 10 }
};

const copy = Object.assign({}, original);

// ネストしたオブジェクトの中身を書き換える
copy.nested.x = 99;

console.log(original.nested.x); // 99
console.log(copy.nested.x);     // 99
console.log(original === copy);           // false(外側は別)
console.log(original.nested === copy.nested); // true(内側は同じ)
JavaScript

ここが重要です:

  • Object.assign は「外側のオブジェクト」を別にするだけ
  • 「中のオブジェクト」は同じものを指している(から、片方を変えるともう片方も変わる)

完全に独立したコピー(ディープコピー)が必要な場合は、Object.assign だけでは足りません。
最初のうちは「浅いコピー」という言葉と、この挙動だけ押さえておけば十分です。


よくある使いどころ(実務のイメージ)

デフォルト設定とユーザー設定のマージ

よくあるパターンは、「デフォルト設定」と「ユーザーからの設定」をマージして、
最終的な設定オブジェクトを作るケースです。

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

function makeOptions(userOptions) {
  return Object.assign({}, defaultOptions, userOptions);
}

const opts = makeOptions({ timeout: 5000, method: "POST" });

console.log(opts);
// { method: "POST", timeout: 5000, cache: "no-cache" }
console.log(defaultOptions); // 元は変わらない
JavaScript

ここが重要です:

  • 左側(defaultOptions)の値が基本になる
  • 右側(userOptions)の値で、同じキーがあれば上書きされる
  • コピー先が {} なので、defaultOptions 自体は壊れない

既存のオブジェクトを「拡張」する

既にあるオブジェクトに、追加情報を後から付け足すこともできます。

const user = { id: 1, name: "Alice" };
const extra = { role: "admin", active: true };

Object.assign(user, extra);

console.log(user);
// { id: 1, name: "Alice", role: "admin", active: true }
JavaScript

ただし、この書き方は user 自体を書き換えてしまうので、
「元を絶対に変えたくない」場面では向きません。
「意図的に拡張したい」場合にだけ使うようにしましょう。


Object.assign とスプレッド構文の違い

ES6 には { ...obj } というスプレッド構文もあります。
これと Object.assign は、できることがかなり似ています。

よく使うパターン比較

コピーだけならこうなります。

const original = { a: 1, b: 2 };

// Object.assign
const copy1 = Object.assign({}, original);

// スプレッド
const copy2 = { ...original };
JavaScript

マージ(結合)ならこうです。

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

// Object.assign
const merged1 = Object.assign({}, a, b);

// スプレッド
const merged2 = { ...a, ...b };
JavaScript

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

ここが重要です:

  • ES5 時代から存在するのが Object.assign
  • ES6 以降の「新しい書き方」として好まれるのがスプレッド { ...obj }

新しくコードを書くときは、読みやすさの観点からスプレッドを使うことが増えていますが、
Object.assign も古いコードやライブラリの中で普通に出てくるので、
「何をしているか」は理解できるようになっておくと安心です。


注意すべきポイント(重要部分の深掘り)

コピー先(第1引数)は必ず「書き換えられる」

const target = { a: 1 };
const source = { b: 2 };

const result = Object.assign(target, source);

console.log(target); // { a: 1, b: 2 }
console.log(result); // { a: 1, b: 2 }(target と同じ)
JavaScript

ここが重要です:

  • Object.assign は「第1引数を変更する関数」
  • 戻り値も第1引数と同じオブジェクト

第1引数を「新しいオブジェクトにしたい」のか、「既存オブジェクトに上書きしたい」のか、
意図をはっきりさせてから使う必要があります。

継承されたプロパティはコピーしない(自分のプロパティだけ)

Object.assign は、「オブジェクト自身の“列挙可能なプロパティ”」だけをコピーします。
プロトタイプチェーン上のプロパティ(一種の“継承されたプロパティ”)はコピーされません。

初心者向けには、
「普通に {} で作ったオブジェクト同士なら、素直に期待どおりコピーされる」
くらいの理解で十分です。

null / undefined をコピー元にするときは無視される

コピー元として nullundefined を渡すと、無視されます(エラーにはならない)。

const target = { a: 1 };

Object.assign(target, null, { b: 2 }, undefined);

console.log(target); // { a: 1, b: 2 }
JavaScript

例題で理解を固める

// 1) 単純なマージ
const t1 = { a: 1 };
const s1 = { b: 2 };
Object.assign(t1, s1);
console.log(t1); // { a: 1, b: 2 }

// 2) 複数マージと上書き
const t2 = { x: 0 };
const s2 = { x: 1, y: 2 };
const s3 = { x: 3 };
const r2 = Object.assign({}, t2, s2, s3);
console.log(r2); // { x: 3, y: 2 }

// 3) デフォルトオプション+ユーザーオプション
const defaultCfg = { theme: "light", fontSize: 14 };
const userCfg = { fontSize: 16 };
const cfg = Object.assign({}, defaultCfg, userCfg);
console.log(cfg); // { theme: "light", fontSize: 16 }

// 4) 浅いコピーの確認
const state = { user: { name: "Alice" }, count: 0 };
const stateCopy = Object.assign({}, state);
stateCopy.user.name = "Bob";
console.log(state.user.name);     // "Bob"(ネストは共有)
console.log(stateCopy.user.name); // "Bob"

// 5) 既存オブジェクトを拡張(破壊的)
const user = { id: 1, name: "Alice" };
Object.assign(user, { role: "admin" });
console.log(user); // { id: 1, name: "Alice", role: "admin" }
JavaScript

まとめ

Object.assign の核心は、
「第1引数のオブジェクトに、右側のオブジェクトのプロパティを左から右へ順番にコピーしていく」
というシンプルなルールです。

重要なポイントを整理すると、

  • コピー先(第1引数)は必ず書き換えられる
  • 同じキーがあれば、後ろのほうが優先されて上書きされる
  • 空オブジェクト {} をコピー先にすると「元を壊さない新オブジェクト」が作れる
  • 浅いコピーなので、ネストしたオブジェクトや配列は共有される
  • スプレッド { ...obj } と役割が似ていて、古いコードほど Object.assign が多く出てくる

という形になります。

スプレッド構文と並べて考えることで、「何をしている関数なのか」が直感的に掴めるようになるので、
ぜひ両方のパターンで書いてみて、動きの違いを手で確認してみてください。

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