object-assignの挙動について勘違いしていたこと

仕様をきちんと理解していれば自明のことですが、ハマりかけたのでメモしておきます。

##この記事の概要
複数のオブジェクトを合成して新しいオブジェクトを返してくれる便利機能Object.assign
現在はharmonyオプション付きのNode.jsでも使えないので、object-assignなどのモジュールを経由して使っていると思います。

私が勘違いしていたのですが、これは

引数として渡したオブジェクトを合成したオブジェクトを返す

という機能ではなく、

第二引数以降に渡したオブジェクトを、第一引数に渡したオブジェクトに合成して返す

ものなのですね。

MDNに記載されているリファレンスにも

1つ以上のソースオブジェクトの保有する全ての列挙プロパティの値を、ターゲットのオブジェクトへコピーします。
戻り値はターゲットオブジェクトになります。

と、明記されています。 Object.assign()

コードで表すと

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
"use strict";
var objectAssign = require('object-assign');
var obj = {
foo: "bar"
};
var obj2 = {
baz: "foobar"
};
console.log(obj); // { foo: 'bar' }
console.log(obj2); // { baz: 'foobar' }
var obj3 = objectAssign(obj, obj2);
console.log(obj3); // { foo: 'bar', baz: 'foobar' }
console.log(obj); // { foo: 'bar', baz: 'foobar' } objの中身も変わっている

obj3に返されているのはobj2を合成したobjですので、当然の挙動です。
objの中身を変更したくないのであれば、次のような書き方をする必要があります。

1
2
3
4
5
var target = {};
var obj4 = objectAssign(target, obj, obj2);
console.log(obj4); // { foo: 'bar', baz: 'foobar' }
console.log(obj); // { foo: 'bar' } objの中身は変わっていない

React.jsで作っているサイトで、Inline Stylesを導入してみて、初めてこの挙動を知った次第です。
きちんと仕様を理解するのが大事、というお話でした。