Object.assign()方法用于將所有可枚舉的屬性的值從一個(gè)或多個(gè)源對(duì)象復(fù)制到目標(biāo)對(duì)象。它將返回目標(biāo)對(duì)象。
語法
Object.assign(target, ...sources)
參數(shù)
target
目標(biāo)對(duì)象。
sources
(多個(gè))源對(duì)象。
返回值
目標(biāo)對(duì)象。
描述
如果目標(biāo)對(duì)象中的屬性具有相同的鍵,則屬性將被源中的屬性覆蓋。后來的源的屬性將類似地覆蓋早先的屬性。
Object.assign方法只會(huì)拷貝源對(duì)象自身的并且可枚舉的屬性到目標(biāo)對(duì)象身上。該方法使用源對(duì)象的 [ [ Get ] ] 和目標(biāo)對(duì)象的 [ [ Set ] ],所以它會(huì)調(diào)用相關(guān) getter 和 setter。因此,它分配屬性而不是復(fù)制或定義新的屬性。如果合并源包含了 getter,那么該方法就不適合將新屬性合并到原型里。假如是拷貝屬性定義到原型里,包括它們的可枚舉性,那么應(yīng)該使用Object.getOwnPropertyDescriptor()和Object.defineProperty()。
String類型和Symbol類型的屬性都會(huì)被拷貝。
注意,在屬性拷貝過程中可能會(huì)產(chǎn)生異常,比如目標(biāo)對(duì)象的某個(gè)只讀屬性和源對(duì)象的某個(gè)屬性同名,這時(shí)該方法會(huì)拋出一個(gè)TypeError異常,拷貝過程中斷,已經(jīng)拷貝成功的屬性不會(huì)受到影響,還未拷貝的屬性將不會(huì)再被拷貝。
注意,Object.assign會(huì)跳過那些值為null或undefined的源對(duì)象。
示例
復(fù)制一個(gè) object
varobj={a:1};varcopy=Object.assign({},obj);console.log(copy);// { a: 1 }
深度拷貝問題
針對(duì)深度拷貝,需要使用其他方法,因?yàn)镺bject.assign() 拷貝的是屬性值。假如源對(duì)象的屬性值是一個(gè)指向?qū)ο蟮囊?,它也只拷貝那個(gè)引用值。
functiontest(){leta={b:{c:4},d:{e:{f:1}}}letg=Object.assign({},a)leth=JSON.parse(JSON.stringify(a));console.log(g.d)// { e: { f: 1 } }g.d.e=32console.log('g.d.e set to 32.')// g.d.e set to 32.console.log(g)// { b: { c: 4 }, d: { e: 32 } }console.log(a)// { b: { c: 4 }, d: { e: 32 } }console.log(h)// { b: { c: 4 }, d: { e: { f: 1 } } }h.d.e=54console.log('h.d.e set to 54.')// h.d.e set to 54.console.log(g)// { b: { c: 4 }, d: { e: 32 } }console.log(a)// { b: { c: 4 }, d: { e: 32 } }console.log(h)// { b: { c: 4 }, d: { e: 54 } }}test();
合并?objects
varo1={a:1};varo2={b:2};varo3={c:3};varobj=Object.assign(o1,o2,o3);console.log(obj);// { a: 1, b: 2, c: 3 }console.log(o1);// { a: 1, b: 2, c: 3 }, 注意目標(biāo)對(duì)象自身也會(huì)改變。
拷貝 symbol 類型的屬性
varo1={a:1};varo2={[Symbol("foo")]:2};varobj=Object.assign({},o1,o2);console.log(obj);// { a: 1, [Symbol("foo")]: 2 }
繼承屬性和不可枚舉屬性是不能拷貝的
varobj=Object.create({foo:1},{// foo 是個(gè)繼承屬性。bar:{value:2// bar 是個(gè)不可枚舉屬性。},baz:{value:3,enumerable:true// baz 是個(gè)自身可枚舉屬性。}});varcopy=Object.assign({},obj);console.log(copy);// { baz: 3 }
原始類型會(huì)被包裝為 object
varv1="abc";varv2=true;varv3=10;varv4=Symbol("foo")varobj=Object.assign({},v1,null,v2,undefined,v3,v4);// 原始類型會(huì)被包裝,null 和 undefined 會(huì)被忽略。// 注意,只有字符串的包裝對(duì)象才可能有自身可枚舉屬性。console.log(obj);// { "0": "a", "1": "b", "2": "c" }
異常會(huì)打斷接下來的拷貝任務(wù)
vartarget=Object.defineProperty({},"foo",{value:1,writable:false});// target 的 foo 屬性是個(gè)只讀屬性。Object.assign(target,{bar:2},{foo2:3,foo:3,foo3:3},{baz:4});// TypeError: "foo" is read-only// 注意這個(gè)異常是在拷貝第二個(gè)源對(duì)象的第二個(gè)屬性時(shí)發(fā)生的。console.log(target.bar);// 2,說明第一個(gè)源對(duì)象拷貝成功了。console.log(target.foo2);// 3,說明第二個(gè)源對(duì)象的第一個(gè)屬性也拷貝成功了。console.log(target.foo);// 1,只讀屬性不能被覆蓋,所以第二個(gè)源對(duì)象的第二個(gè)屬性拷貝失敗了。console.log(target.foo3);// undefined,異常之后 assign 方法就退出了,第三個(gè)屬性是不會(huì)被拷貝到的。console.log(target.baz);// undefined,第三個(gè)源對(duì)象更是不會(huì)被拷貝到的。
拷貝訪問器(accessor)
varobj={foo:1,getbar(){return2;}};varcopy=Object.assign({},obj);// { foo: 1, bar: 2 }// copy.bar的值來自obj.bar的getter函數(shù)的返回值console.log(copy);// 下面這個(gè)函數(shù)會(huì)拷貝所有自有屬性的屬性描述符functioncompleteAssign(target,...sources){sources.forEach(source=>{letdescriptors=Object.keys(source).reduce((descriptors,key)=>{descriptors[key]=Object.getOwnPropertyDescriptor(source,key);returndescriptors;},{});// Object.assign 默認(rèn)也會(huì)拷貝可枚舉的SymbolsObject.getOwnPropertySymbols(source).forEach(sym=>{letdescriptor=Object.getOwnPropertyDescriptor(source,sym);if(descriptor.enumerable){descriptors[sym]=descriptor;}});Object.defineProperties(target,descriptors);});returntarget;}varcopy=completeAssign({},obj);// { foo:1, get bar() { return 2 } }console.log(copy);
Polyfill
由于 ES5 里壓根就沒有 symbol 這種數(shù)據(jù)類型,所以這個(gè)polyfill也沒必要去支持 symbol 屬性(意思就是說,有 symbol 的環(huán)境一定有原生的Object.assign):
if(typeofObject.assign!='function'){Object.assign=function(target){'use strict';if(target==null){thrownewTypeError('Cannot convert undefined or null to object');}target=Object(target);for(varindex=1;index