ES6 Object.assign
一、基本用法
Object.assign
方法用來將源對象(source
)的所有可枚舉屬性,復制到目標對象(target
)。它至少需要兩個對象作為參數,第一個參數是目標對象,后面的參數都是源對象。只要有一個參數不是對象,就會拋出TypeError錯誤。
var target = { a: 1 }; var source1 = { b: 2 }; var source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
注:如果目標對象與源對象有同名屬性,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性。
var target = { a: 1, b: 1 }; var source1 = { b: 2, c: 2 }; var source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
如果只有一個參數,Object.assign會直接返回該參數。
var obj = {a: 1}; Object.assign(obj) === obj // true
如果該參數不是對象,則會先轉成對象,然后返回。
typeof Object.assign(2) // "object"
由於undefined和null無法轉成對象,所以如果它們作為參數,就會報錯。
Object.assign(undefined) // 報錯 Object.assign(null) // 報錯
如果非對象參數出現在源對象的位置(即非首參數),那么處理規則有所不同。首先,這些參數都會轉成對象,如果無法轉成對象,就會跳過。這意味着,如果undefined和null不在首參數,就不會報錯。其他類型的值(即數值、字符串和布爾值)不在首參數,也不會報錯。但是,除了字符串會以數組形式,拷貝入目標對象,其他值都不會產生效果。
Object.assign
只拷貝自身屬性,不可枚舉的屬性(enumerable
為false
)和繼承的屬性不會被拷貝。
Object.assign({b: 'c'}, Object.defineProperty({}, 'invisible', { enumerable: false, value: 'hello' }) ) // { b: 'c' } Object.assign({b: 'c'}, Object.defineProperty({}, 'invisible', { enumerable: true, value: 'hello' }) ) // {b: "c", invisible: "hello"}
對於嵌套的對象,Object.assign
的處理方法是替換,而不是添加。
var target = { a: { b: 'c', d: 'e' } } var source = { a: { b: 'hello' } } Object.assign(target, source) // { a: { b: 'hello' } }
上面代碼中,target對象的a屬性被source對象的a屬性整個替換掉了,而不會得到{ a: { b: 'hello', d: 'e' } }的結果。這通常不是開發者想要的,需要特別小心。有一些函數庫提供Object.assign的定制版本(比如Lodash
的_.defaultsDeep
方法),可以解決深拷貝的問題。
注意,Object.assign
可以用來處理數組,但是會把數組視為對象。
Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3]
其中,4覆蓋1,5覆蓋2,因為它們在數組的同一位置,所以就對應位置覆蓋了。
Object.assign方法實行的是淺拷貝,而不是深拷貝。也就是說,如果源對象某個屬性的值是對象,那么目標對象拷貝得到的是這個對象的引用。
var obj1 = {a: {b: 1}}; var obj2 = Object.assign({}, obj1); obj1.a.b = 2; obj2.a.b // 2
上面代碼中,源對象obj1的a屬性的值是一個對象,Object.assign拷貝得到的是這個對象的引用。這個對象的任何變化,都會反映到目標對象上面。
二、用途
1. 為對象添加屬性
class Point { constructor(x, y) { Object.assign(this, {x, y}); } }
這樣就給Point
類的對象實例添加了x、y屬性。
2. 為對象添加方法
Object.assign(SomeClass.prototype, { someMethod(arg1, arg2) { ··· }, anotherMethod() { ··· } }); // 等同於下面的寫法 SomeClass.prototype.someMethod = function (arg1, arg2) { ··· }; SomeClass.prototype.anotherMethod = function () { ··· };
上面代碼使用了對象屬性的簡潔表示法,直接將兩個函數放在大括號中,再使用assign
方法添加到SomeClass.prototype
之中。
3. 克隆對象
function clone(origin) { return Object.assign({}, origin); }
上面代碼將原始對象拷貝到一個空對象,就得到了原始對象的克隆。
不過,采用這種方法克隆,只能克隆原始對象自身的值,不能克隆它繼承的值。如果想要保持繼承鏈,可以采用下面的代碼。
function clone(origin) { let originProto = Object.getPrototypeOf(origin); return Object.assign(Object.create(originProto), origin); }
在JS里子類利用Object.getPrototypeOf
去調用父類方法,用來獲取對象的原型。用它可以模仿Java
的super。
4. 合並多個對象
//多個對象合並到某個對象 const merge =(target, ...sources) => Object.assign(target, ...sources); //多個對象合並到新對象 const merge = (...sources) => Object.assign({}, ...sources);
5. 為屬性指定默認值
const DEFAULTS = { logLevel: 0, outputFormat: 'html' }; function processContent(options) { let options = Object.assign({}, DEFAULTS, options); }
上面代碼中,DEFAULTS
對象是默認值,options
對象是用戶提供的參數。Object.assign
方法將DEFAULTS
和options
合並成一個新對象,如果兩者有同名屬性,則option
的屬性值會覆蓋DEFAULTS
的屬性值。
注: 由於存在深拷貝的問題,DEFAULTS
對象和options
對象的所有屬性的值,都只能是簡單類型,而不能指向另一個對象。否則,將導致DEFAULTS
對象的該屬性不起作用。
三、瀏覽器支持
參考: