Object.assign
Object.assign
Object.assign()
方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
语法
Object.assign(target, ...sources)
参数
target
目标对象。sources
源对象。
返回值
目标对象。
描述
如果目标对象中的属性具有相同的键,则属性将被源中的属性覆盖。后来的源的属性将类似地覆盖早先的属性。
Object.assign
方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]
和目标对象的[[Set]]
,所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。如果合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到原型,应使用Object.getOwnPropertyDescriptor()
和Object.defineProperty()
。
String
类型和Symbol
类型的属性都会被拷贝。
在出现错误的情况下,例如,如果属性不可写,会引发TypeError
,如果在引发错误之前添加了任何属性,则可以更改target
对象。
注意,Object.assign
会跳过那些值为null
或undefined
的源对象。
示例
复制一个对象
var obj = { a: 1 };
var copy = Object.assign{}, obj
console.log(copy // { a: 1 }
深拷贝问题
针对深拷贝,需要使用其他方法,因为 Object.assign()
拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。
function test() {
'use strict';
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign{}, obj1
console.log(JSON.stringify(obj2) // { a: 0, b: { c: 0}}
obj1.a = 1;
console.log(JSON.stringify(obj1) // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2) // { a: 0, b: { c: 0}}
obj2.a = 2;
console.log(JSON.stringify(obj1) // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2) // { a: 2, b: { c: 0}}
obj2.b.c = 3;
console.log(JSON.stringify(obj1) // { a: 1, b: { c: 3}}
console.log(JSON.stringify(obj2) // { a: 2, b: { c: 3}}
// Deep Clone
obj1 = { a: 0 , b: { c: 0}};
let obj3 = JSON.parse(JSON.stringify(obj1)
obj1.a = 4;
obj1.b.c = 4;
console.log(JSON.stringify(obj3) // { a: 0, b: { c: 0}}
}
test(
合并对象
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };
var obj = Object.assign(o1, o2, o3
console.log(obj // { a: 1, b: 2, c: 3 }
console.log(o1 // { a: 1, b: 2, c: 3 }, target object itself is changed.
合并具有相同属性的对象
var o1 = { a: 1, b: 1, c: 1 };
var o2 = { b: 2, c: 2 };
var o3 = { c: 3 };
var obj = Object.assign{}, o1, o2, o3
console.log(obj // { a: 1, b: 2, c: 3 }
属性被后续参数中具有相同属性的其他对象覆盖。
拷贝 symbol 类型的属性
var o1 = { a: 1 };
var o2 = { [Symbol('foo')]: 2 };
var obj = Object.assign{}, o1, o2
console.log(obj // { a : 1, [Symbol("foo")]: 2 } (cf. bug 1207182 on Firefox)
Object.getOwnPropertySymbols(obj // [Symbol(foo)]
继承属性和不可枚举属性是不能拷贝的
var obj = Object.create{ foo: 1 }, { // foo is on obj's prototype chain.
bar: {
value: 2 // bar is a non-enumerable property.
},
baz: {
value: 3,
enumerable: true // baz is an own enumerable property.
}
}
var copy = Object.assign{}, obj
console.log(copy // { baz: 3 }
原始类型会被包装为对象
var v1 = 'abc';
var v2 = true;
var v3 = 10;
var v4 = Symbol('foo'
var obj = Object.assign{}, v1, null, v2, undefined, v3, v4
// Primitives will be wrapped, null and undefined will be ignored.
// Note, only string wrappers can have own enumerable properties.
console.log(obj // { "0": "a", "1": "b", "2": "c" }
异常会打断后续拷贝任务
var target = Object.defineProperty{}, 'foo', {
value: 1,
writable: false
} // target.foo is a read-only property
Object.assign(target, { bar: 2 }, { foo2: 3, foo: 3, foo3: 3 }, { baz: 4 }
// TypeError: "foo" is read-only
// The Exception is thrown when assigning target.foo
console.log(target.bar // 2, the first source was copied successfully.
console.log(target.foo2 // 3, the first property of the second source was copied successfully.
console.log(target.foo // 1, exception is thrown here.
console.log(target.foo3 // undefined, assign method has finished, foo3 will not be copied.
console.log(target.baz // undefined, the third source will not be copied either.
拷贝访问器
var obj = {
foo: 1,
get bar() {
return 2;
}
};
var copy = Object.assign{}, obj
console.log(copy
// { foo: 1, bar: 2 }, the value of copy.bar is obj.bar's getter's return value.
// This is an assign function that copies full descriptors
function completeAssign(target, ...sources) {
sources.forEach(source => {
let descriptors = Object.keys(source).reduce((descriptors, key) => {
descriptors[key] = Object.getOwnPropertyDescriptor(source, key
return descriptors;
}, {}
// by default, Object.assign copies enumerable Symbols too
Object.getOwnPropertySymbols(source).forEach(sym => {
let descriptor = Object.getOwnPropertyDescriptor(source, sym
if (descriptor.enumerable) {
descriptors[sym] = descriptor;
}
}
Object.defineProperties(target, descriptors
}
return target;
}
var copy = completeAssign{}, obj
console.log(copy
// { foo:1, get bar() { return 2 } }
Polyfill
此 polyfill 不支持 symbol 属性,因为ES5 中根本没有 symbol :
if (typeof Object.assign != 'function') {
// Must be writable: true, enumerable: false, configurable: true
Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) { // .length of function is 2
'use strict';
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object'
}
var to = Object(target
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true
}
}
规范
Specification | Status | Comment |
---|---|---|
ECMAScript 2015 (6th Edition, ECMA-262)The definition of 'Object.assign' in that specification. | Standard | Initial definition. |
ECMAScript Latest Draft (ECMA-262)The definition of 'Object.assign' in that specification. | Living Standard | |
浏览器兼容性
Feature | Chrome | Edge | Firefox | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|---|
Basic Support | 45 | (Yes) | 34 | No | 32 | 9 |
Feature | Android | Chrome for Android | Edge mobile | Firefox for Android | IE mobile | Opera Android | iOS Safari |
---|---|---|---|---|---|---|---|
Basic Support | No | 45 | (Yes) | (Yes) | No | No | (Yes) |