摘要:一篇文章徹底說清的深拷貝淺拷貝這篇文章的受眾第一類業務需要急需知道如何深拷貝對象的開發者。這篇文章分享的目的更多還是希望用一篇文章整理清楚深淺拷貝的含義遞歸實現思路以及小伙伴們如果使用了這種黑科技一定要清楚這樣寫的優缺點。
一篇文章徹底說清JS的深拷貝and淺拷貝 這篇文章的受眾
第一類,業務需要,急需知道如何深拷貝JS對象的開發者。
第二類,希望扎實JS基礎,將來好去面試官前秀操作的好學者。
寫給第一類讀者你只需要一行黑科技代碼就可以實現深拷貝
var copyObj = { name: "ziwei", arr : [1,2,3] } var targetObj = JSON.parse(JSON.stringify(copyObj)) 此時 copyObj.arr !== targetObj.arr 已經實現了深拷貝
別著急走,利用window.JSON的方法做深拷貝存在2個缺點:
如果你的對象里有函數,函數無法被拷貝下來
無法拷貝copyObj對象原型鏈上的屬性和方法
當然,你明確知道他們的缺點后,如果他的缺點對你的業務需求沒有影響,就可以放心使用了,一行原生代碼就能搞定。
目前我在開發業務場景中,大多還真可以忽略上面2個缺點。往往需要深拷貝的對象里沒有函數,也不需要拷貝它原型鏈的屬性。
寫給第二類讀者下面我會盡可能全面的講解清楚JS里對象的拷貝,要講清楚拷貝,你需要一點點前置知識
你需要的前置知識:
理解JS里的引用類型和值類型的區別,知道Obj存儲的只是引用
對原型鏈有基本了解
關于對象拷貝的全部:
1.深拷貝、淺拷貝是什么
2.深拷貝、淺拷貝在業務里的最常見的應用場景
3.深拷貝和淺拷貝的實現方式
4.總結與建議
1.深拷貝、淺拷貝是什么我們討論JS對象深拷貝、淺拷貝的前提
只有對象里嵌套對象的情況下,才會根據需求討論,我們要深拷貝還是淺拷貝。
比如下面這種對象
var obj1 = { name: "ziwei", arr : [1,2,3] }
因為,如果是類似這樣{name: "ziwei"},沒有嵌套對象的對象的話,就沒必要區分深淺拷貝了。只有在有嵌套的對象時,深拷貝和淺拷貝才有區別
淺拷貝是什么樣子的 (我們暫時不管具體如何實現,因為下面會單講)
調用shallowCopy()后,obj2拷貝obj1所有的屬性。但是obj2.arr和obj1.arr是不同的引用,指向同一個內存空間
var obj2 = shallowCopy( obj1 , {}) console.log( obj1 !== obj2 ) // true 無論哪種拷貝,obj1和obj2一定都是2個不同的對象(內存空間不同) console.log( obj2.arr === obj1.arr ) // true 他們2個對象里arr的引用,指向【相同的】內存空間
所以, 2個obj經過拷貝后,雖然他們屬性相同,也的確是不同的對象,但他們內部的obj都是指向同一個內存空間,這種我們叫淺拷貝
深拷貝是什么樣子的 (我們暫時不管具體如何實現,因為下面會單講)
調用deepCopy()后,obj2拷貝obj1所有的屬性,而且obj2.arr和obj1.arr是指向不同的內存空間,
2個obj2除了拷貝了一樣的屬性,沒有任何其他關聯。
var obj2 = deepCopy( obj1 , {}) console.log( obj1 !== obj2 ) // true 無論哪種拷貝,obj1和obj2一定都是2個不同的對象(內存空間不同) console.log( obj2.arr === obj1.arr ) // false 他們2個對象里arr的引用,指向【不同的】內存空間
所以, 2個obj經過拷貝后,除了拷貝下來相同的屬性之外,沒有任何其他關聯的2個對象,這種我們叫深拷貝
2.深拷貝在業務里的最常見的應用場景舉個栗子,業務需求是 : 一個表格展示商品各種信息,點擊【同意】時,是可以彈出對話框調整商品數量的。
這種業務需求下,我們就會用到對象的深拷貝。因為【商品表格】的屬性和【調整商品表格】的屬性幾乎一樣,我們需要拷貝。
下面的偽代碼和圖片就是展示使用淺拷貝存在的問題
這樣得到的adjustTableArr和tableArr里,內部對象都是相同的,所以就出現了圖中紅線標注的情況,
當我們修改【調整商品表格】里的商品數量時,【商品表格】也跟著改變了,這并不是我們想要的
// 表格對象的數據結構 var tableArr = [ {goods_name : "長袖腰背夾" , code : "M216C239E0864" , num : "2"}, {goods_name : "長袖腰背夾" , code : "M216C240B0170" , num : "3"}, {goods_name : "短塑褲" , code : "M216D241C04106" , num : "3"}, ] var adjustTableArr = [] // 調整表格用的數組 for (var key in tableArr) { // 淺拷貝 adjustTableArr[key] = tableArr[key] }
而實際上,我們希望這2個表格里的數據完全獨立,互不干擾,只有在確認調整之后才刷新商品數量。
這種情況下我們就可以使用前面說的深拷貝的一行黑科技
var adjustTableArr = JSON.parse(JSON.stringify(tableArr))
還記得它的缺陷嗎? 對象里的函數無法被拷貝,原型鏈里的屬性無法被拷貝。這里就對業務沒有影響,可以很方便的深拷貝。
3.深拷貝和淺拷貝的實現方式其實JQ里已經有$.extend()函數,實現就是深拷貝和淺拷貝的功能。有興趣的小伙伴也可以看看源碼。
淺拷貝
淺拷貝比較簡單,就是用for in 循環賦值
function shallowCopy(source, target = {}) { var key; for (key in source) { if (source.hasOwnProperty(key)) { // 意思就是__proto__上面的屬性,我不拷貝 target[key] = source[key]; } } return target; }
深拷貝的實現
深拷貝,就是遍歷那個被拷貝的對象
判斷對象里每一項的數據類型
如果不是對象類型,就直接賦值,如果是對象類型,就再次調用deepCopy,遞歸的去賦值。
function deepCopy(source, target = {}) { var key; for (key in source) { if (source.hasOwnProperty(key)) { // 意思就是__proto__上面的屬性,我不拷貝 if (typeof(source[key]) === "object") { // 如果這一項是object類型,就遞歸調用deepCopy target[key] = Array.isArray(source[key]) ? [] : {}; deepCopy(source[key], target[key]); } else { // 如果不是object類型,就直接賦值拷貝 target[key] = source[key]; } } } return target; }
以上的無論深、淺拷貝,都用了source.hasOwnProperty(key),意思是判斷這一項是否是其自有屬性,是的話才拷貝,不是就不拷貝。
也就是說__proto__上面的屬性,我不拷貝。這個其實你可以根據業務需求,來決定加上和這個條件
(JQ的$.extend()是會連__proto__上的屬性也拷貝下來的,但是是直接拷貝到對象上,而不是放到之前的__proto__上)
4.總結與建議雖然大家可能經常用框架提供的api來實現深拷貝。
這篇文章分享的目的,更多還是希望用一篇文章整理清楚深淺拷貝的含義、遞歸實現思路,以及小伙伴們如果使用了JSON.parse()這種黑科技,一定要清楚這樣寫的優缺點。
5.修正上面的deepCopy方法有漏洞,沒有考慮source一開始就是數組的情況
下面是一個修改后版本
function deepCopy( source ) { let target = Array.isArray( source ) ? [] : {} for ( var k in source ) { if ( typeof source[ k ] === "object" ) { target[ k ] = deepCopy( source[ k ] ) } else { target[ k ] = source[ k ] } } return target }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/112992.html
摘要:一篇文章徹底說清的深拷貝淺拷貝這篇文章的受眾第一類業務需要急需知道如何深拷貝對象的開發者。這篇文章分享的目的更多還是希望用一篇文章整理清楚深淺拷貝的含義遞歸實現思路以及小伙伴們如果使用了這種黑科技一定要清楚這樣寫的優缺點。 一篇文章徹底說清JS的深拷貝and淺拷貝 這篇文章的受眾 第一類,業務需要,急需知道如何深拷貝JS對象的開發者。 第二類,希望扎實JS基礎,將來好去面試官前秀操作...
摘要:一篇文章徹底說清的深拷貝淺拷貝這篇文章的受眾第一類業務需要急需知道如何深拷貝對象的開發者。這篇文章分享的目的更多還是希望用一篇文章整理清楚深淺拷貝的含義遞歸實現思路以及小伙伴們如果使用了這種黑科技一定要清楚這樣寫的優缺點。 一篇文章徹底說清JS的深拷貝and淺拷貝 這篇文章的受眾 第一類,業務需要,急需知道如何深拷貝JS對象的開發者。 第二類,希望扎實JS基礎,將來好去面試官前秀操作...
摘要:案例中的賦值就是典型的淺拷貝,并且深拷貝與淺拷貝的概念只存在于引用類型。修改修改經測試,也只能實現一維對象的深拷貝。經過驗證,我們發現提供的自有方法并不能徹底解決的深拷貝問題。 在說深拷貝與淺拷貝前,我們先看兩個簡單的案例: //案例1 var num1 = 1, num2 = num1; console.log(num1) //1 console.log(num2) //1 num...
摘要:而引用類型值是指那些保存堆內存中的對象,意思是變量中保存的實際上只是一個指針,這個指針指向內存中的另一個位置,該位置保存對象。而堆內存主要負責對象這種變量類型的存儲。我們需要明確一點,深拷貝與淺拷貝的概念只存在于引用類型。 深拷貝和淺拷貝 說起深拷貝和淺拷貝,首先我們來看兩個栗子 // 栗子1 var a = 1,b=a; console.log(a); console.log(b) ...
摘要:引用類型值指的是那些保存在堆內存中的對象,所以引用類型的值保存的是一個指針,這個指針指向存儲在堆中的一個對象。因此當操作結束后,這兩個變量實際上指向的是同一個在堆內存中的對象,改變其中任意一個對象,另一個對象也會跟著改變。 一、為什么有深拷貝和淺拷貝? ???? 這個要從js中的數據類型說起,js中數據類型分為基本數據類型和引用數據類型。 ????基本類型值指的是那些保存在棧內存中的簡...
閱讀 2633·2021-11-23 09:51
閱讀 888·2021-09-24 10:37
閱讀 3618·2021-09-02 15:15
閱讀 1966·2019-08-30 13:03
閱讀 1886·2019-08-29 15:41
閱讀 2632·2019-08-29 14:12
閱讀 1429·2019-08-29 11:19
閱讀 3307·2019-08-26 13:39