成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

低門檻徹底理解JavaScript中的深拷貝和淺拷貝

開發(fā) 前端
Javascript深拷貝和淺拷貝是初學者甚至有經驗的開發(fā)者,都會經常遇到問題,本文作者帶你低門檻徹底理解JavaScript中的深拷貝和淺拷貝。

[[228890]]

在說深拷貝與淺拷貝前,我們先看兩個簡單的案例:

 

  1. //案例1  
  2. var num1 = 1, num2 = num1;  
  3. console.log(num1) //1  
  4. console.log(num2) //1  
  5. num2 = 2; //修改num2  
  6. console.log(num1) //1  
  7. console.log(num2) //2  
  8. //案例2  
  9. var obj1 = {x: 1, y: 2}, obj2 = obj1;  
  10. console.log(obj1) //{x: 1, y: 2}  
  11. console.log(obj2) //{x: 1, y: 2}  
  12. obj2.x = 2; //修改obj2.x  
  13. console.log(obj1) //{x: 2, y: 2}  
  14. console.log(obj2) //{x: 2, y: 2} 

按照常規(guī)思維,obj1應該和num1一樣,不會因為另外一個值的改變而改變,而這里的obj1 卻隨著obj2的改變而改變了。同樣是變量,為什么表現不一樣呢?這就要引入JS中基本類型和引用類型的概念了。

基本類型和引用類型

ECMAScript變量可能包含兩種不同數據類型的值:基本類型值和引用類型值。基本類型值指的是那些保存在棧內存中的簡單數據段,即這種值完全保存在內存中的一個位置。而引用類型值是指那些保存堆內存中的對象,意思是變量中保存的實際上只是一個指針,這個指針指向內存中的另一個位置,該位置保存對象。

打個比方,基本類型和引用類型在賦值上的區(qū)別可以按“連鎖店”和“單店”來理解:基本類型賦值等于在一個新的地方安裝連鎖店的規(guī)范標準新開一個分店,新開的店與其他舊店互不相關,各自運營;而引用類型賦值相當于一個店有兩把鑰匙,交給兩個老板同時管理,兩個老板的行為都有可能對一間店的運營造成影響。

上面清晰明了的介紹了基本類型和引用類型的定義和區(qū)別。目前基本類型有:

Boolean、Null、Undefined、Number、String、Symbol,引用類型有:Object、Array、Function。之所以說“目前”,因為Symbol就是ES6才出來的,之后也可能會有新的類型出來。

再回到前面的案例,案例1中的值為基本類型,案例2中的值為引用類型。案例2中的賦值就是典型的淺拷貝,并且深拷貝與淺拷貝的概念只存在于引用類型。

深拷貝與淺拷貝

既然已經知道了深拷貝與淺拷貝的來由,那么該如何實現深拷貝?我們先分別看看Array和Object自有方法是否支持:

Array  

 

  1. var arr1 = [1, 2], arr2 = arr1.slice();  
  2. console.log(arr1); //[1, 2]  
  3. console.log(arr2); //[1, 2]  
  4. arr2[0] = 3; //修改arr2  
  5. console.log(arr1); //[1, 2]  
  6. console.log(arr2); //[3, 2] 

此時,arr2的修改并沒有影響到arr1,看來深拷貝的實現并沒有那么難嘛。我們把arr1改成二維數組再來看看:

 

  1. var arr1 = [1, 2, [3, 4]], arr2 = arr1.slice();  
  2. console.log(arr1); //[1, 2, [3, 4]]  
  3. console.log(arr2); //[1, 2, [3, 4]]  
  4. arr2[2][1] = 5;   
  5. console.log(arr1); //[1, 2, [3, 5]]  
  6. console.log(arr2); //[1, 2, [3, 5]] 

咦,arr2又改變了arr1,看來slice()只能實現一維數組的深拷貝。

具備同等特性的還有:concat、Array.from() 。

Object

1、Object.assign()

 

  1. var obj1 = {x: 1, y: 2}, obj2 = Object.assign({}, obj1);  
  2. console.log(obj1) //{x: 1, y: 2}  
  3. console.log(obj2) //{x: 1, y: 2}  
  4. obj2.x = 2; //修改obj2.x  
  5. console.log(obj1) //{x: 1, y: 2}  
  6. console.log(obj2) //{x: 2, y: 2}  
  7. var obj1 = {  
  8.     x: 1,   
  9.     y: {  
  10.         m: 1  
  11.     }  
  12. };  
  13. var obj2 = Object.assign({}, obj1);  
  14. console.log(obj1) //{x: 1, y: {m: 1}}  
  15. console.log(obj2) //{x: 1, y: {m: 1}}  
  16. obj2.y.m = 2; //修改obj2.y.m  
  17. console.log(obj1) //{x: 1, y: {m: 2}}  
  18. console.log(obj2) //{x: 2, y: {m: 2}} 

經測試,Object.assign()也只能實現一維對象的深拷貝。

2、JSON.parse(JSON.stringify(obj))

 

  1. var obj1 = {  
  2.     x: 1,   
  3.     y: {  
  4.         m: 1  
  5.     }  
  6. };  
  7. var obj2 = JSON.parse(JSON.stringify(obj1));  
  8. console.log(obj1) //{x: 1, y: {m: 1}}  
  9. console.log(obj2) //{x: 1, y: {m: 1}}  
  10. obj2.y.m = 2; //修改obj2.y.m  
  11. console.log(obj1) //{x: 1, y: {m: 1}}  
  12. console.log(obj2) //{x: 2, y: {m: 2}} 

JSON.parse(JSON.stringify(obj)) 看起來很不錯,不過MDN文檔 的描述有句話寫的很清楚:

undefined、任意的函數以及 symbol 值,在序列化過程中會被忽略(出現在非數組對象的屬性值中時)或者被轉換成 null(出現在數組中時)。

我們再來把obj1改造下:

 

  1. var obj1 = {  
  2.     x: 1,  
  3.     y: undefined,  
  4.     z: function add(z1, z2) {  
  5.         return z1 + z2  
  6.     },  
  7.     a: Symbol("foo" 
  8. };  
  9. var obj2 = JSON.parse(JSON.stringify(obj1));  
  10. console.log(obj1) //{x: 1, y: undefined, z: ƒ, a: Symbol(foo)}  
  11. console.log(JSON.stringify(obj1)); //{"x":1}  
  12. console.log(obj2) //{x: 1} 

發(fā)現,在將obj1進行JSON.stringify()序列化的過程中,y、z、a都被忽略了,也就驗證了MDN文檔的描述。既然這樣,那JSON.parse(JSON.stringify(obj))的使用也是有局限性的,不能深拷貝含有undefined、function、symbol值的對象,不過JSON.parse(JSON.stringify(obj))簡單粗暴,已經滿足90%的使用場景了。

經過驗證,我們發(fā)現JS 提供的自有方法并不能徹底解決Array、Object的深拷貝問題。只能祭出大殺器:遞歸

 

  1. function deepCopy(obj) {  
  2.     // 創(chuàng)建一個新對象  
  3.     let result = {}  
  4.     let keys = Object.keys(obj),  
  5.         key = null 
  6.         temp = null 
  7.     for (let i = 0; i < keys.length; i++) {  
  8.         key = keys[i];      
  9.         temp = obj[key];  
  10.         // 如果字段的值也是一個對象則遞歸操作  
  11.         if (temp && typeof temp === 'object') {  
  12.             result[key] = deepCopy(temp);  
  13.         } else {  
  14.         // 否則直接賦值給新對象  
  15.             result[key] = temp 
  16.         }  
  17.     }  
  18.     return result;  
  19.  
  20. var obj1 = {  
  21.     x: {  
  22.         m: 1  
  23.     },  
  24.     y: undefined,  
  25.     z: function add(z1, z2) {  
  26.         return z1 + z2  
  27.     },  
  28.     a: Symbol("foo" 
  29. };  
  30. var obj2 = deepCopy(obj1);  
  31. obj2.x.m = 2;  
  32. console.log(obj1); //{x: {m: 1}, y: undefined, z: ƒ, a: Symbol(foo)}  
  33. console.log(obj2); //{x: {m: 2}, y: undefined, z: ƒ, a: Symbol(foo)} 

可以看到,遞歸***的解決了前面遺留的所有問題,我們也可以用第三方庫:jquery的$.extend和lodash的_.cloneDeep來解決深拷貝。上面雖然是用Object驗證,但對于Array也同樣適用,因為Array也是特殊的Object。

到這里,深拷貝問題基本可以告一段落了。但是,還有一個非常特殊的場景:

循環(huán)引用拷貝

 

  1. var obj1 = {  
  2.     x: 1,   
  3.     y: 2  
  4. };  
  5. obj1.z = obj1;  
  6. var obj2 = deepCopy(obj1); 

此時如果調用剛才的deepCopy函數的話,會陷入一個循環(huán)的遞歸過程,從而導致爆棧。jquery的$.extend也沒有解決。解決這個問題也非常簡單,只需要判斷一個對象的字段是否引用了這個對象或這個對象的任意父級即可,修改一下代碼:

 

  1. function deepCopy(obj, parent = null) {  
  2.     // 創(chuàng)建一個新對象  
  3.     let result = {};  
  4.     let keys = Object.keys(obj),  
  5.         key = null 
  6.         tempnull 
  7.         _parent = parent;  
  8.     // 該字段有父級則需要追溯該字段的父級  
  9.     while (_parent) {  
  10.         // 如果該字段引用了它的父級則為循環(huán)引用  
  11.         if (_parent.originalParent === obj) {  
  12.             // 循環(huán)引用直接返回同級的新對象  
  13.             return _parent.currentParent;  
  14.         }  
  15.         _parent = _parent.parent;  
  16.     }  
  17.     for (let i = 0; i < keys.length; i++) {  
  18.         key = keys[i];  
  19.         temp= obj[key];  
  20.         // 如果字段的值也是一個對象  
  21.         if (temp && typeof temp=== 'object') {  
  22.             // 遞歸執(zhí)行深拷貝 將同級的待拷貝對象與新對象傳遞給 parent 方便追溯循環(huán)引用  
  23.             result[key] = DeepCopy(temp, {  
  24.                 originalParent: obj,  
  25.                 currentParent: result,  
  26.                 parent: parent  
  27.             });  
  28.         } else { 
  29.             result[key] = temp 
  30.         }  
  31.     }  
  32.     return result;  
  33.  
  34. var obj1 = {  
  35.     x: 1,   
  36.     y: 2  
  37. };  
  38. obj1.z = obj1;  
  39. var obj2 = deepCopy(obj1);  
  40. console.log(obj1); //太長了去瀏覽器試一下吧~   
  41. console.log(obj2); //太長了去瀏覽器試一下吧~  

 

 

至此,已完成一個支持循環(huán)引用的深拷貝函數。當然,也可以使用lodash的_.cloneDeep噢~。 

責任編輯:龐桂玉 來源: 前端大全
相關推薦

2020-10-12 08:35:22

JavaScript

2021-07-16 12:33:24

Javascript深拷貝淺拷貝

2018-09-26 14:37:17

JavaScript前端編程語言

2017-08-16 13:30:05

Java深拷貝淺拷貝

2024-03-15 15:03:23

2009-05-19 17:28:44

深拷貝淺拷貝clone()

2023-05-17 08:42:46

深拷貝Golang

2025-04-27 09:45:58

JavaScript深拷貝淺拷貝

2020-06-23 08:41:47

JavaScript開發(fā)技術

2021-09-27 11:07:11

深拷貝淺拷貝內存

2022-07-26 08:07:03

Python淺拷貝深拷貝

2017-05-24 11:54:55

Javascript深拷貝

2024-04-17 09:01:08

Python深拷貝淺拷貝

2020-08-03 08:24:26

原型模式拷貝

2021-01-08 06:15:09

深拷貝淺拷貝寫時拷貝

2022-09-30 15:03:09

C語言深拷貝淺拷貝

2024-02-05 22:56:16

C++拷貝開發(fā)

2023-05-17 07:36:00

淺拷貝深拷貝對象

2023-09-22 12:21:33

Python深拷貝淺拷貝

2023-05-08 09:00:46

JSON深拷貝對象
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品大全 | 成人在线免费观看 | 91久久精品日日躁夜夜躁欧美 | 国产激情免费视频 | 天天拍天天操 | 国产久| av一区二区三区四区 | 欧美群妇大交群中文字幕 | 亚洲 中文 欧美 日韩 在线观看 | 亚洲一区播放 | 欧美日韩一区不卡 | 99久久久国产精品 | 美女黄网站 | 久久精品国产v日韩v亚洲 | 日韩欧美一二三区 | 欧美成人免费 | 一区二区av | 天天干天天色 | 五月精品视频 | av网站免费 | 欧美4p| 国产精品久久久久久久久久久久 | 久久久久久久久久久久久久国产 | 色婷婷综合网 | 久久99这里只有精品 | 国产综合精品 | 亚洲天堂免费在线 | av网站在线免费观看 | 福利电影在线 | 99热视| 久久久久久国产精品久久 | 欧美黄色性生活视频 | 999久久精品| 国产成人在线一区二区 | 澳门永久av免费网站 | 国产视频一区在线 | 欧美黑人国产人伦爽爽爽 | 美女国产精品 | 特黄色毛片 | 欧美精品在欧美一区二区少妇 | 久久亚洲一区二区三区四区 |