摘要:一篇文章徹底說(shuō)清的深拷貝淺拷貝這篇文章的受眾第一類業(yè)務(wù)需要急需知道如何深拷貝對(duì)象的開(kāi)發(fā)者。這篇文章分享的目的更多還是希望用一篇文章整理清楚深淺拷貝的含義遞歸實(shí)現(xiàn)思路以及小伙伴們?nèi)绻褂昧诉@種黑科技一定要清楚這樣寫的優(yōu)缺點(diǎn)。
一篇文章徹底說(shuō)清JS的深拷貝and淺拷貝 這篇文章的受眾
第一類,業(yè)務(wù)需要,急需知道如何深拷貝JS對(duì)象的開(kāi)發(fā)者。
第二類,希望扎實(shí)JS基礎(chǔ),將來(lái)好去面試官前秀操作的好學(xué)者。
寫給第一類讀者你只需要一行黑科技代碼就可以實(shí)現(xiàn)深拷貝
var copyObj = { name: "ziwei", arr : [1,2,3] } var targetObj = JSON.parse(JSON.stringify(copyObj)) 此時(shí) copyObj.arr !== targetObj.arr 已經(jīng)實(shí)現(xiàn)了深拷貝
別著急走,利用window.JSON的方法做深拷貝存在2個(gè)缺點(diǎn):
如果你的對(duì)象里有函數(shù),函數(shù)無(wú)法被拷貝下來(lái)
無(wú)法拷貝copyObj對(duì)象原型鏈上的屬性和方法
當(dāng)然,你明確知道他們的缺點(diǎn)后,如果他的缺點(diǎn)對(duì)你的業(yè)務(wù)需求沒(méi)有影響,就可以放心使用了,一行原生代碼就能搞定。
目前我在開(kāi)發(fā)業(yè)務(wù)場(chǎng)景中,大多還真可以忽略上面2個(gè)缺點(diǎn)。往往需要深拷貝的對(duì)象里沒(méi)有函數(shù),也不需要拷貝它原型鏈的屬性。
寫給第二類讀者下面我會(huì)盡可能全面的講解清楚JS里對(duì)象的拷貝,要講清楚拷貝,你需要一點(diǎn)點(diǎn)前置知識(shí)
你需要的前置知識(shí):
理解JS里的引用類型和值類型的區(qū)別,知道Obj存儲(chǔ)的只是引用
對(duì)原型鏈有基本了解
關(guān)于對(duì)象拷貝的全部:
1.深拷貝、淺拷貝是什么
2.深拷貝、淺拷貝在業(yè)務(wù)里的最常見(jiàn)的應(yīng)用場(chǎng)景
3.深拷貝和淺拷貝的實(shí)現(xiàn)方式
4.總結(jié)與建議
1.深拷貝、淺拷貝是什么我們討論JS對(duì)象深拷貝、淺拷貝的前提
只有對(duì)象里嵌套對(duì)象的情況下,才會(huì)根據(jù)需求討論,我們要深拷貝還是淺拷貝。
比如下面這種對(duì)象
var obj1 = { name: "ziwei", arr : [1,2,3] }
因?yàn)?如果是類似這樣{name: "ziwei"},沒(méi)有嵌套對(duì)象的對(duì)象的話,就沒(méi)必要區(qū)分深淺拷貝了。只有在有嵌套的對(duì)象時(shí),深拷貝和淺拷貝才有區(qū)別
淺拷貝是什么樣子的 (我們暫時(shí)不管具體如何實(shí)現(xiàn),因?yàn)橄旅鏁?huì)單講)
調(diào)用shallowCopy()后,obj2拷貝obj1所有的屬性。但是obj2.arr和obj1.arr是不同的引用,指向同一個(gè)內(nèi)存空間
var obj2 = shallowCopy( obj1 , {}) console.log( obj1 !== obj2 ) // true 無(wú)論哪種拷貝,obj1和obj2一定都是2個(gè)不同的對(duì)象(內(nèi)存空間不同) console.log( obj2.arr === obj1.arr ) // true 他們2個(gè)對(duì)象里arr的引用,指向【相同的】?jī)?nèi)存空間
所以, 2個(gè)obj經(jīng)過(guò)拷貝后,雖然他們屬性相同,也的確是不同的對(duì)象,但他們內(nèi)部的obj都是指向同一個(gè)內(nèi)存空間,這種我們叫淺拷貝
深拷貝是什么樣子的 (我們暫時(shí)不管具體如何實(shí)現(xiàn),因?yàn)橄旅鏁?huì)單講)
調(diào)用deepCopy()后,obj2拷貝obj1所有的屬性,而且obj2.arr和obj1.arr是指向不同的內(nèi)存空間,
2個(gè)obj2除了拷貝了一樣的屬性,沒(méi)有任何其他關(guān)聯(lián)。
var obj2 = deepCopy( obj1 , {}) console.log( obj1 !== obj2 ) // true 無(wú)論哪種拷貝,obj1和obj2一定都是2個(gè)不同的對(duì)象(內(nèi)存空間不同) console.log( obj2.arr === obj1.arr ) // false 他們2個(gè)對(duì)象里arr的引用,指向【不同的】?jī)?nèi)存空間
所以, 2個(gè)obj經(jīng)過(guò)拷貝后,除了拷貝下來(lái)相同的屬性之外,沒(méi)有任何其他關(guān)聯(lián)的2個(gè)對(duì)象,這種我們叫深拷貝
2.深拷貝在業(yè)務(wù)里的最常見(jiàn)的應(yīng)用場(chǎng)景舉個(gè)栗子,業(yè)務(wù)需求是 : 一個(gè)表格展示商品各種信息,點(diǎn)擊【同意】時(shí),是可以彈出對(duì)話框調(diào)整商品數(shù)量的。
這種業(yè)務(wù)需求下,我們就會(huì)用到對(duì)象的深拷貝。因?yàn)椤旧唐繁砀瘛康膶傩院汀菊{(diào)整商品表格】的屬性幾乎一樣,我們需要拷貝。
下面的偽代碼和圖片就是展示使用淺拷貝存在的問(wèn)題
這樣得到的adjustTableArr和tableArr里,內(nèi)部對(duì)象都是相同的,所以就出現(xiàn)了圖中紅線標(biāo)注的情況,
當(dāng)我們修改【調(diào)整商品表格】里的商品數(shù)量時(shí),【商品表格】也跟著改變了,這并不是我們想要的
// 表格對(duì)象的數(shù)據(jù)結(jié)構(gòu) var tableArr = [ {goods_name : "長(zhǎng)袖腰背夾" , code : "M216C239E0864" , num : "2"}, {goods_name : "長(zhǎng)袖腰背夾" , code : "M216C240B0170" , num : "3"}, {goods_name : "短塑褲" , code : "M216D241C04106" , num : "3"}, ] var adjustTableArr = [] // 調(diào)整表格用的數(shù)組 for (var key in tableArr) { // 淺拷貝 adjustTableArr[key] = tableArr[key] }
而實(shí)際上,我們希望這2個(gè)表格里的數(shù)據(jù)完全獨(dú)立,互不干擾,只有在確認(rèn)調(diào)整之后才刷新商品數(shù)量。
這種情況下我們就可以使用前面說(shuō)的深拷貝的一行黑科技
var adjustTableArr = JSON.parse(JSON.stringify(tableArr))
還記得它的缺陷嗎? 對(duì)象里的函數(shù)無(wú)法被拷貝,原型鏈里的屬性無(wú)法被拷貝。這里就對(duì)業(yè)務(wù)沒(méi)有影響,可以很方便的深拷貝。
3.深拷貝和淺拷貝的實(shí)現(xiàn)方式其實(shí)JQ里已經(jīng)有$.extend()函數(shù),實(shí)現(xiàn)就是深拷貝和淺拷貝的功能。有興趣的小伙伴也可以看看源碼。
淺拷貝
淺拷貝比較簡(jiǎn)單,就是用for in 循環(huán)賦值
function shallowCopy(source, target = {}) { var key; for (key in source) { if (source.hasOwnProperty(key)) { // 意思就是__proto__上面的屬性,我不拷貝 target[key] = source[key]; } } return target; }
深拷貝的實(shí)現(xiàn)
深拷貝,就是遍歷那個(gè)被拷貝的對(duì)象
判斷對(duì)象里每一項(xiàng)的數(shù)據(jù)類型
如果不是對(duì)象類型,就直接賦值,如果是對(duì)象類型,就再次調(diào)用deepCopy,遞歸的去賦值。
function deepCopy(source, target = {}) { var key; for (key in source) { if (source.hasOwnProperty(key)) { // 意思就是__proto__上面的屬性,我不拷貝 if (typeof(source[key]) === "object") { // 如果這一項(xiàng)是object類型,就遞歸調(diào)用deepCopy target[key] = Array.isArray(source[key]) ? [] : {}; deepCopy(source[key], target[key]); } else { // 如果不是object類型,就直接賦值拷貝 target[key] = source[key]; } } } return target; }
以上的無(wú)論深、淺拷貝,都用了source.hasOwnProperty(key),意思是判斷這一項(xiàng)是否是其自有屬性,是的話才拷貝,不是就不拷貝。
也就是說(shuō)__proto__上面的屬性,我不拷貝。這個(gè)其實(shí)你可以根據(jù)業(yè)務(wù)需求,來(lái)決定加上和這個(gè)條件
(JQ的$.extend()是會(huì)連__proto__上的屬性也拷貝下來(lái)的,但是是直接拷貝到對(duì)象上,而不是放到之前的__proto__上)
4.總結(jié)與建議雖然大家可能經(jīng)常用框架提供的api來(lái)實(shí)現(xiàn)深拷貝。
這篇文章分享的目的,更多還是希望用一篇文章整理清楚深淺拷貝的含義、遞歸實(shí)現(xiàn)思路,以及小伙伴們?nèi)绻褂昧薐SON.parse()這種黑科技,一定要清楚這樣寫的優(yōu)缺點(diǎn)。
5.修正上面的deepCopy方法有漏洞,沒(méi)有考慮source一開(kāi)始就是數(shù)組的情況
下面是一個(gè)修改后版本
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 }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/51690.html
摘要:一篇文章徹底說(shuō)清的深拷貝淺拷貝這篇文章的受眾第一類業(yè)務(wù)需要急需知道如何深拷貝對(duì)象的開(kāi)發(fā)者。這篇文章分享的目的更多還是希望用一篇文章整理清楚深淺拷貝的含義遞歸實(shí)現(xiàn)思路以及小伙伴們?nèi)绻褂昧诉@種黑科技一定要清楚這樣寫的優(yōu)缺點(diǎn)。 一篇文章徹底說(shuō)清JS的深拷貝and淺拷貝 這篇文章的受眾 第一類,業(yè)務(wù)需要,急需知道如何深拷貝JS對(duì)象的開(kāi)發(fā)者。 第二類,希望扎實(shí)JS基礎(chǔ),將來(lái)好去面試官前秀操作...
摘要:一篇文章徹底說(shuō)清的深拷貝淺拷貝這篇文章的受眾第一類業(yè)務(wù)需要急需知道如何深拷貝對(duì)象的開(kāi)發(fā)者。這篇文章分享的目的更多還是希望用一篇文章整理清楚深淺拷貝的含義遞歸實(shí)現(xiàn)思路以及小伙伴們?nèi)绻褂昧诉@種黑科技一定要清楚這樣寫的優(yōu)缺點(diǎn)。 一篇文章徹底說(shuō)清JS的深拷貝and淺拷貝 這篇文章的受眾 第一類,業(yè)務(wù)需要,急需知道如何深拷貝JS對(duì)象的開(kāi)發(fā)者。 第二類,希望扎實(shí)JS基礎(chǔ),將來(lái)好去面試官前秀操作...
摘要:案例中的賦值就是典型的淺拷貝,并且深拷貝與淺拷貝的概念只存在于引用類型。修改修改經(jīng)測(cè)試,也只能實(shí)現(xiàn)一維對(duì)象的深拷貝。經(jīng)過(guò)驗(yàn)證,我們發(fā)現(xiàn)提供的自有方法并不能徹底解決的深拷貝問(wèn)題。 在說(shuō)深拷貝與淺拷貝前,我們先看兩個(gè)簡(jiǎn)單的案例: //案例1 var num1 = 1, num2 = num1; console.log(num1) //1 console.log(num2) //1 num...
摘要:而引用類型值是指那些保存堆內(nèi)存中的對(duì)象,意思是變量中保存的實(shí)際上只是一個(gè)指針,這個(gè)指針指向內(nèi)存中的另一個(gè)位置,該位置保存對(duì)象。而堆內(nèi)存主要負(fù)責(zé)對(duì)象這種變量類型的存儲(chǔ)。我們需要明確一點(diǎn),深拷貝與淺拷貝的概念只存在于引用類型。 深拷貝和淺拷貝 說(shuō)起深拷貝和淺拷貝,首先我們來(lái)看兩個(gè)栗子 // 栗子1 var a = 1,b=a; console.log(a); console.log(b) ...
摘要:引用類型值指的是那些保存在堆內(nèi)存中的對(duì)象,所以引用類型的值保存的是一個(gè)指針,這個(gè)指針指向存儲(chǔ)在堆中的一個(gè)對(duì)象。因此當(dāng)操作結(jié)束后,這兩個(gè)變量實(shí)際上指向的是同一個(gè)在堆內(nèi)存中的對(duì)象,改變其中任意一個(gè)對(duì)象,另一個(gè)對(duì)象也會(huì)跟著改變。 一、為什么有深拷貝和淺拷貝? ???? 這個(gè)要從js中的數(shù)據(jù)類型說(shuō)起,js中數(shù)據(jù)類型分為基本數(shù)據(jù)類型和引用數(shù)據(jù)類型。 ????基本類型值指的是那些保存在棧內(nèi)存中的簡(jiǎn)...
閱讀 4062·2021-11-22 13:53
閱讀 3661·2021-11-19 11:29
閱讀 1336·2021-09-08 09:35
閱讀 3214·2020-12-03 17:26
閱讀 543·2019-08-29 16:06
閱讀 2142·2019-08-26 13:50
閱讀 1216·2019-08-23 18:32
閱讀 2182·2019-08-23 18:12