JavaScriptでは、オブジェクトが格納されている変数を他の変数にコピーすると、そのオブジェクトへの参照がコピーされる。
そのため、元となるオブジェクトの値を変更すると、コピー先のオブジェクトにも変更が反映されてしまう。逆も然り。
let obj1 = {value:'hoge'}; let obj2 = obj1; obj1.value = 'fuga'; console.log(obj2); // { value: 'fuga' }
だがES2015で追加されたObject.assign()
を使うと、参照ではなく値をコピーできる。
値が同じだけの全く別のオブジェクトなので、一方で行った変更の影響は、もう一方には及ばない。
let obj1 = {value: 'hoge'} let obj2 = Object.assign({}, obj1); console.log(obj2); // { value: 'hoge' } obj1.value = 'fuga'; console.log(obj1); // { value: 'fuga' } console.log(obj2); // { value: 'hoge' }
Object.assign()
の第一引数に空のオブジェクトを渡し、第二引数にコピー元のオブジェクトを渡すと、コピーされたオブジェクトを返す。
obj1
とobj2
は別個のオブジェクトなので、obj1
のプロパティを変更しても、obj2
は影響を受けない。
注意点は、プロパティにオブジェクトがあると、そのオブジェクトは参照渡しになってしまう。
直下のプロパティをコピーしていく、というのがObject.assign()
の挙動のようなので、そこにオブジェクトがあると、参照がコピーされてしまう。
let obj1 = { value: 'hoge', obj2: { num: 1 } }; let copy = Object.assign({}, obj1); console.log(copy); // { value: 'hoge', obj2: { num: 1 } } obj1.value = 'fuga'; obj1.obj2.num = 9; console.log(copy); // { value: 'hoge', obj2: { num: 9 } }
value
は値がコピーされているが、obj2
は参照渡しになっているのが確認できる。
Object.assign()の詳細
Object.assign()
の本来の用途は、オブジェクトをマージすることである。その機能を利用することで、上記のようなオブジェクトのコピーにも使える、ということである。
第一引数に渡したオブジェクトに、第二引数以降のオブジェクトのプロパティを追加していく。
第一引数に渡したオブジェクトが変更される
let obj1 = {value:'hoge'}; let obj2 = {}; Object.assign(obj2, obj1); console.log(obj2); // { value: 'hoge' }
Object.assign()
を実行した時点で、第一引数のオブジェクトは変更される。
実行前の状態は残らないので、注意が必要。
そして、変更後のオブジェクトが、そのまま戻り値になる。
対象となるのは、列挙可能な自身のプロパティ
マージされるのは、列挙可能(Enumerable
)な自身のプロパティのみである。列挙不可能なものは、対象にならない。
let obj1 = {value:'hoge', num:1}; console.log(obj1.propertyIsEnumerable('value')); // true console.log(Object.assign({}, obj1)); // { value: 'hoge', num: 1 } Object.defineProperty(obj1, 'value', { enumerable: false }); console.log(obj1.propertyIsEnumerable('value')); // false console.log(Object.assign({}, obj1)); // { num: 1 }
value
のenumerable
がfalse
になるとマージされなくなっているのが分かる。
プロパティの内部属性であるenumerable
については、下記を参照。
プロパティの内部属性
競合した場合は上書きされていく
第一引数のオブジェクトに対して、第二引数以降のオブジェクトが順次マージされていくが、その過程で同名のプロパティがあった場合、上書きされていく。
let obj1 = {value: 'hoge'}; let obj2 = {value: 'fuga'}; let obj3 = {num: 1, bool: true}; let obj4 = {value: 'boo'}; Object.assign(obj1, obj2); console.log(obj1); // { value: 'fuga' } obj1 = {value: 'hoge'}; console.log(obj1); // { value: 'hoge' } Object.assign(obj1, obj2, obj3); console.log(obj1); // { value: 'fuga', num: 1, bool: true } obj1 = {value: 'hoge'}; console.log(obj1); // { value: 'hoge' } Object.assign(obj1, obj2, obj3, obj4); console.log(obj1); // { value: 'boo', num: 1, bool: true }