最近再寫ES6的文章時候發現自己對Object.assign()方法不太了解,之前也沒有接觸過所以就就查閱了相關的資料,為了自己以后肯能會用到以及對知識進行鞏固,所以在這里記錄下自己學習的點點滴滴,畢竟好記性不如然筆筒,廢話不多說,直接上干貨。
官方解釋:Object.assign方法用於將所有可枚舉屬性的值從一個或多個源對象復制到目標對象。它將返回目標對象。
語法:Object.assign(target,...sources)
參數:
target:目標對象
sources:源對象
返回值:目標對象
描述:如果目標對象中的屬性具有相同的鍵,則屬性將被源對象中的屬性覆蓋,后面的源對象的屬性將覆蓋前面的源對象的屬性
Object.assign
方法只會拷貝源對象自身的並且可枚舉的屬性到目標對象。該方法使用源對象的[[Get]]
和目標對象的[[Set]]
,所以它會調用相關 getter 和 setter。因此,它分配屬性,而不僅僅是復制或定義新的屬性。如果合並源包含getter,這可能使其不適合將新屬性合並到原型中。為了將屬性定義(包括其可枚舉性)復制到原型,應使用Object.getOwnPropertyDescriptor()
和Object.defineProperty()
。
注意:
- Object.assign不會在那些sources對象值為null或undefined的時候拋出錯誤
- 在出現錯誤的情況下,例如,如果屬性不可寫,會引發
TypeError
,如果在引發錯誤之前添加了任何屬性,則可以更改target
對象 String
類型和Symbol
類型的屬性都會被拷貝
作用
- 復制一個對象
- 實現深拷貝
- 合並對象
- 合並具有相同屬性的對象
- 拷貝 symbol 類型的屬性
- 繼承屬性和不可枚舉屬性是不能拷貝的
- 原始類型會被包裝為對象
- 異常會打斷后續拷貝任務
- 拷貝訪問器
(1):復制一個對象
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>復制一個對象</title> </head> <body> <script type="text/javascript"> const obj={ a:1, eat:function(){ console.log('吃東西') } } const copy=Object.assign({},obj); console.log(copy.a);//1 copy.eat();//吃東西 copy.a=10; console.log(copy.a);//10 console.log(obj.a);//1 </script> </body> </html>
我們首先定義了一個obj對象,然后使用Object.assign方法拷貝了obj這個對象對copy中,發現copy中具有obj的屬性和方法,當我們修改copy中a的值的時候發現obj中a的值沒有發生改變,由此可以證明Object.assign實現的是深拷貝,重新在內存中開辟新的空間,而不是拷貝原來對象的地址。
(2):實現深拷貝
針對深拷貝,需要使用其他辦法,因為 Object.assign()
拷貝的是屬性值。假如源對象的屬性值是一個對象的引用,那么它也只指向那個引用
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>實現深拷貝</title> </head> <body> <script type="text/javascript"> 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":0,"b":{"c":0}} console.log(JSON.stringify(obj2));//{"a":0,"b":{"c":0}} obj2.a=2; console.log(JSON.stringify(obj1));//{"a":0,"b":{"c":0}} console.log(JSON.stringify(obj2));//{"a":0,"b":{"c":0}} obj2.a=3; console.log(JSON.stringify(obj1));//{"a":0,"b":{"c":0}} console.log(JSON.stringify(obj2));//{"a":0,"b":{"c":0}} //深拷貝 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}} </script> </body> </html>
(3):合並對象
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>合並對象</title> </head> <body> <script type="text/javascript"> const o1={a:1}; const o2={b:2}; const o3={c:3}; const obj=Object.assign(o1,o2,o3); console.log(obj);//{a:1,b:2,c:3} console.log(o1);//{a:1,b:2,c:3} console.log(o2);//{b:2} console.log(o3);//{c:3} </script> </body> </html>
在這里我們需要注意的是目標對象自身也會改變,即示例中的o1
(4):合並具有相同屬性的對象
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>合並具有相同屬性的對象</title> </head> <body> <script type="text/javascript"> const o1={a:1,b:1,c:1}; const o2={b:2,c:2}; const o3={c:3}; const obj=Object.assign(o1,o2,o3); console.log(obj);//1,2,3 console.log(o1);//1,2,3 </script> </body> </html>
如果目標對象中的屬性具有相同的鍵,則屬性將被源對象中的屬性覆蓋,后面的源對象的屬性將覆蓋前面的源對象的屬性,這句話已經解釋的很清楚了,我們看到o1中都有a,b,c三個屬性,o2中具有b,c兩個屬性,o3中具有c一個屬性,當合並的時候發現目標對象具有相同的鍵,所以被覆蓋了。
(5):拷貝 symbol 類型的屬性
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>拷貝symbol類型的屬性</title> </head> <body> <script type="text/javascript"> const o1={a:1}; const o2={[Symbol('foo')]:2}; const obj=Object.assign({},o1,o2); console.log(obj);// { a : 1, [Symbol("foo")]: 2 } console.log(Object.getOwnPropertySymbols(obj));// [Symbol(foo)] </script> </body> </html>
在這里的話,可能對symbol會有些陌生,不過不要緊后續的博客中我會講到,現在的話我可以告訴你這也是一種用來定義類型的類型
(6):繼承屬性和不可枚舉屬性是不能拷貝的
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>繼承屬性和不可枚舉屬性是不能拷貝的</title> </head> <body> <script type="text/javascript"> const obj=Object.create({foo:1},{ //foo 是個繼承屬性 bar:{ value:2 //bar 是個不可枚舉屬性 }, baz:{ // baz 是個自身可枚舉屬性 value:3, enumerable:true, } }); const copy=Object.assign({},obj); console.log(copy);//{baz:3} </script> </body> </html>
在這里我們知道foo是個繼承的屬性,而bar是不可枚舉屬性,baz是個可枚舉屬性,所以最終只拷貝了baz對象
(7):原始類型會被包裝為對象
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>原始類型會被包裝為對象</title> </head> <body> <script type="text/javascript"> const v1="abc"; const v2=true; const v3=10; const v4=Symbol('foo'); const obj=Object.assign({},v1,null,v2,undefined,v3,v4); console.log(obj);//{0:a,1:b,2:c} </script> </body> </html>
在這里主要總結兩點:1.原始類型會進行包裝,null和undefined會被忽略,2.只有字符串的包裝對象才可能有自身可枚舉屬性
(8):異常會打斷后續拷貝任務
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>異常會打斷后續拷貝任務</title> </head> <body> <script type="text/javascript"> const target=Object.defineProperty({},"foo",{ value:1, writeable:true }); Object.assign(target,{bar:2},{foo2:3,foo:3,foo3:3},{baz:4}); console.log(target.bar);//2,說明第一個源對象拷貝成功了。 console.log(target.foo2);//3,說明第二個源對象的第一個屬性也拷貝成功了。 console.log(target.foo);//1,只讀屬性不能被覆蓋,所以第二個源對象的第二個屬性拷貝失敗了。 console.log(target.foo3);//undefined,異常之后 assign 方法就退出了,第三個屬性是不會被拷貝到的。 console.log(target.baz);// undefined,第三個源對象更是不會被拷貝到的。 </script> </body> </html>
首先我們先來看第一個對象,在第一個對象中我們定義了target的屬性只讀不可寫,然后使用Object.assign方法實現拷貝發現bar拷貝成功了,然后繼續拷貝,foo2也拷貝成功了,當碰到foo的時候發現目標對象也是foo,本來是會覆蓋的但是我們設置了只讀不可寫,所以到foo的時候拷貝就失敗了,接着就發生了異常,導致后面的拷貝失敗了,所以foo3和baz的值為undefined
(9):拷貝訪問器
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>拷貝訪問器</title> </head> <body> <script type="text/javascript"> const obj = { foo: 1, get bar() { return 2; } }; let copy = Object.assign({}, obj); console.log(copy); // { foo: 1, bar: 2 } copy.bar的值來自obj.bar的getter函數的返回值 </script> </body> </html>
在這里我們看到Object.assign()拷貝了對象的訪問器,即get bar()函數