js Object.assign()究竟是深拷貝還是淺拷貝


之前我有對object函數相關的常見方法做過總結,感興趣的同學可以通過鏈接查看:

https://www.cnblogs.com/zhilu/p/13842177.html

一、今天對Object.assign()做一個分析總結。

1、定義:Object.assign() 方法用於將所有可枚舉屬性的值從一個或多個源對象復制到目標對象。它將返回目標對象。

2、Object.assign()拷貝

Object.assign()拷貝的是 屬性值。假如源對象的屬性值是一個對象的引用,那么它也只指向那個引用。也就是說,如果對象的屬性值為 簡單類型(如string, number),通過Object.assign({},srcObj);得到的新對象為深拷貝;如果屬性值為 對象或其它引用類型,那對於這個對象而言其實是淺拷貝的。
 
也就是說,當對象中只有 一級屬性,沒有二級屬性的時候,此方法為 深拷貝
但是對象中有 二級屬性的時候,此方法,在二級屬性以后就是 淺拷貝

 

  

3、所以,當對象中有二級屬性的時候, 使用遞歸的方式實現深拷貝

二、延伸:怎樣實現深拷貝呢

1、對象深拷貝的幾種方法
 
1)用 JSON.stringify 把對象轉換成字符串,再用 JSON.parse 把字符串轉換成新的對象,但是需要注意的是,可以轉成 JSON 格式的對象才能使用這種方法, 如果對象中包含 function 或 RegExp 這些就不能用這種方法了。
1 function deepClone2(obj) {
2   let _obj = JSON.stringify(obj),
3   return JSON.parse(_obj);
4 }

缺點:

(1)如果對象里有函數,函數無法被拷貝下來

(2)無法拷貝copyObj對象原型鏈上的屬性和方法

(3)當數據的層次很深,會棧溢出

2)使用普通遞歸的方式實現深拷貝
 1 function deepClone(obj){
 2   let objClone =  Array.isArray(obj) ? [] : {};
 3   if (obj && typeof obj === 'object') {
 4     for(let key in obj){
 5       if (obj[key] && typeof obj[key] === 'object'){
 6         objClone[key] = deepClone(obj[key]);
 7       }else{
 8         objClone[key] = obj[key]
 9       }
10     }
11   }
12   return objClone;
13 }

缺點:

(1)無法保持引用

(2)當數據的層次很深,會棧溢出

3)防棧溢出函數

 1 function cloneLoop(x) {
 2     const root = {};
 3 
 4     // 棧
 5     const loopList = [
 6         {
 7             parent: root,
 8             key: undefined,
 9             data: x,
10         }
11     ];
12 
13     while(loopList.length) {
14         // 深度優先
15         const node = loopList.pop();
16         const parent = node.parent;
17         const key = node.key;
18         const data = node.data;
19 
20         // 初始化賦值目標,key為undefined則拷貝到父元素,否則拷貝到子元素
21         let res = parent;
22         if (typeof key !== 'undefined') {
23             res = parent[key] = {};
24         }
25 
26         for(let k in data) {
27             if (data.hasOwnProperty(k)) {
28                 if (typeof data[k] === 'object') {
29                     // 下一次循環
30                     loopList.push({
31                         parent: res,
32                         key: k,
33                         data: data[k],
34                     });
35                 } else {
36                     res[k] = data[k];
37                 }
38             }
39         }
40     }
41 
42     return root;
43 }

優點:

(1)不會棧溢出

(2)支持很多層級的數據

 1  function copyObject(orig) {
 2     var copy = Object.create(Object.getPrototypeOf(orig));
 3     copyOwnPropertiesFrom(copy, orig);
 4     return copy;
 5   }
 6 
 7 
 8   function copyOwnPropertiesFrom(target, source) {
 9     Object
10     .getOwnPropertyNames(source)
11     .forEach(function (propKey) {
12       var desc = Object.getOwnPropertyDescriptor(source, propKey);
13       Object.defineProperty(target, propKey, desc);
14     });
15     return target;
16   }
17 
18   var obj = {
19     name: 'Jack',
20     age: '32',
21     job: 'developer'
22   };
23 
24   var obj2 = copyObject(obj);
25   console.log(obj2);
26   obj.age = 39;
27   obj.name = 'Tom';
28   console.log(obj);
29   console.log(obj2);

4)通過jQuery的extend方法實現深拷貝

 1 var obj = {
 2     a: '123',
 3     b: 234,
 4     c: true,
 5     d: null,
 6     e: function() {console.log('test')},
 7     h: new Set([4,3,null]),
 8     i:Symbol('fsd'),
 9     k: new Map([ ["name", "test"],  ["title", "Author"]  ])
10 }
11 console.log($.extend(true, {}, obj))

5)lodash函數庫實現深拷貝

1 let clone = cloneDeep(obj)

2、數組深拷貝的幾種方法

1)concat(arr1, arr2,....)

2)slice(idx1, idx2)

1)沒有參數是拷貝數組

2)只有一個參數是從該位置起到結束拷貝數組元素

3)兩個參數,拷貝從起始位置到結束位置的元素(不包含結束位置的元素:含頭不含尾)

需要注意的是,slice()和concat()這兩個方法,僅適用於對不包含引用對象的一維數組的深拷貝

 

三、賦值、淺拷貝與深拷貝的區別

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM