JavaScript最完整的深淺拷貝實(shí)現(xiàn)方式詳解
深淺拷貝:
內(nèi)存中一共分為棧內(nèi)存和堆內(nèi)存兩大區(qū)域,所謂深淺拷貝主要是對(duì)js引用類型數(shù)據(jù)進(jìn)行拷貝一份,淺拷貝就是引用類型數(shù)據(jù)相互賦值之后,例obj1=obj2;如果后面的操作中修改obj1或者obj2,這個(gè)時(shí)候數(shù)據(jù)是會(huì)進(jìn)行相應(yīng)的變化的,因?yàn)樵趦?nèi)存中引用類型數(shù)據(jù)是存儲(chǔ)在堆內(nèi)存中,堆內(nèi)存中存放的是引用類型的值,同時(shí)會(huì)有一個(gè)指針地址指向棧內(nèi)存,兩個(gè)引用類型數(shù)據(jù)地址一樣,如果其中一個(gè)發(fā)生變化另外一個(gè)都會(huì)有影響;而深拷貝則不會(huì),深拷貝是會(huì)在堆內(nèi)存中重新開辟一塊空間進(jìn)行存放;
簡(jiǎn)單來說就是B復(fù)制了A,如果A發(fā)生了改變,如果B隨之變化,那么是淺拷貝,如果B并沒有發(fā)生變化,則是深拷貝。
基本類型拷貝
let a = 1; let b = a; b = 2; console.log(a);//1 console.log(b);//2
a,b都是屬于基本類型,基本類型的復(fù)制是不會(huì)影響對(duì)方的,因?yàn)榛绢愋褪敲恳淮蝿?chuàng)建變量都會(huì)在棧內(nèi)存中開辟一塊內(nèi)存,用來存放值,所以對(duì)基本類型進(jìn)行拷貝是不會(huì)對(duì)另外一個(gè)變量有影響的,屬于深拷貝。
數(shù)組拷貝
concat() slice()
// concat() let list = ['a','b','c']; let list2 = list.concat(); list2.push('d') console.log(list);//['a','b','c'] console.log(list2);//['a','b','c','d'] // slice() let list = ['a','b','c']; let list2 = list.slice(); list2.push('d') console.log(list);//['a','b','c'] console.log(list2);//['a','b','c','d']
上面兩種方法只能實(shí)現(xiàn)數(shù)組類型里面的單層深拷貝,如果是多層無法實(shí)現(xiàn)深拷貝。
//二維數(shù)組 let list = ['a','b','c',['d','e','f']]; let list2 = list.concat(); list2[3][0] = 'a'; console.log(list); console.log(list2);
可以看到原二維數(shù)組的值也發(fā)生了變化,說明沒有實(shí)現(xiàn)深拷貝。
由此總結(jié),concat和slice只能實(shí)現(xiàn)一維數(shù)組的深拷貝,不能對(duì)多維數(shù)組進(jìn)行深拷貝,所以并不是一個(gè)完美的深拷貝方式。
對(duì)象拷貝
new Object()
let a = {id:1,name:'a',obj:{id:999}}; let b = new Object(); b.id = a.id; b.name = a.name; b.obj = a.obj; a.name = 'b'; a.obj.id = 888; console.log(a); console.log(b);
a.name更改并沒有影響到b.name,好像可以看作深拷貝,但第二層obj里面里面的id隨之更改了,因此其實(shí)并不是深拷貝。
Object.assign
let a = {id:1,name:'a',obj:{id:999}}; function fun(obj){ let o = {}; Object.assign(o,obj); return o; } let a2 = fun(a); a2.name ='a2'; a2.obj.id = 888; console.log(a); console.log(a2);
以上兩種方法,對(duì)于一層對(duì)象都能實(shí)現(xiàn)深拷貝,但對(duì)于多層對(duì)象則無法實(shí)現(xiàn),因此也不是一種完美的深拷貝方式。
JSON.parse(JSON.stringify( ))
let a = { name : 'a', age : 20, obj : {id:999}, action : function(){ console.log(this.name); } } let b = JSON.parse(JSON.stringify(a)); a.name = 'b'; a.obj.id = 888; console.log(a); console.log(b);
單層對(duì)象name和多層對(duì)象obj.id的改變都沒影響到原對(duì)象,因此是一個(gè)比較完美的深拷貝,但是能看到function好像并沒有被拷貝。
可以看出這個(gè)方法對(duì)于一層和多層都能實(shí)現(xiàn)深拷貝,但是這個(gè)方法的缺陷是不能拷貝Function,所以在使用時(shí),一定要注意數(shù)據(jù)類型。
遞歸
let a = { name:'a', skin:["red","blue","yellow",["123","456"]], child:{ work:'none', obj:{ id:999 } }, action:function(){ console.log(this.name); } } //封裝的遞歸方法 function copyWid(obj){ let newObj = Array.isArray(obj)?[]:{}; for (var i in obj){ if(typeof obj[i] === 'object'){ //判斷是不是對(duì)象(數(shù)組或?qū)ο螅? newObj[i] = copyWid(obj[i]) //遞歸解決多層拷貝 }else{ newObj[i] = obj[i] } } return newObj; }; let b = copyWid(a); b.child.obj.id =888; b.skin[3][0] = "pink"; console.log(a); console.log(b);
可以看到,無論是多層的對(duì)象還是多層的數(shù)組,都能實(shí)現(xiàn)深拷貝,而且能拷貝函數(shù),這個(gè)是目前來說最完美的深拷貝方法。
通過這個(gè)遞歸能實(shí)現(xiàn)一個(gè)比較完美的深拷貝,能彌補(bǔ)上述提到的所有方法中的缺點(diǎn)。
展開運(yùn)算符
let a = {name:'a',id:99};//如果是數(shù)組[xx,xx,xx],{...a}需要改成[...a] let b ={...a}; //[...a] a.id =88; console.log(a); console.log(b);
這個(gè)方法能實(shí)現(xiàn)對(duì)象數(shù)組單層時(shí)的深拷貝,但是多層無法實(shí)現(xiàn)深拷貝。
以上這么多方法,最優(yōu)解應(yīng)該屬于JSON和遞歸的方法(遞歸解決了JSON無法拷貝函數(shù)方法的問題),但是根據(jù)需求數(shù)據(jù)類型的不同,可以選擇更簡(jiǎn)單的方式。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Javascript 網(wǎng)頁黑白效果實(shí)現(xiàn)代碼(兼容IE/FF等)
今天在網(wǎng)上看到有人推薦的一個(gè)不錯(cuò)的方法,試了一下,效果還是可以的,可以自定義讓網(wǎng)頁的某一部分變成灰度顏色(黑白)。2010-04-04巧用js提交表單輕松解決一個(gè)頁面有多個(gè)提交按鈕
使用js提交表單想必大家都會(huì),如果要實(shí)現(xiàn)一個(gè)頁面有多個(gè)提交按鈕,你會(huì)嗎?下面有個(gè)不錯(cuò)的示例,大家可以感受下2013-11-11Web前端框架bootstrap實(shí)戰(zhàn)【第一次接觸使用】
Bootstrap是Twitter推出的一個(gè)開源的前端框架。這篇文章主要介紹了Web前端框架bootstrap實(shí)戰(zhàn),需要的朋友可以參考下2016-12-12JS實(shí)現(xiàn)漂亮的淡藍(lán)色滑動(dòng)門效果代碼
這篇文章主要介紹了JS實(shí)現(xiàn)漂亮的淡藍(lán)色滑動(dòng)門效果代碼,涉及JavaScript通過自定義函數(shù)遍歷頁面元素及動(dòng)態(tài)設(shè)置元素屬性的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09快速學(xué)習(xí)JavaScript的6個(gè)思維技巧
在這篇文章中,我將介紹六個(gè)思維技巧來幫助你更快地學(xué)習(xí)JavaScript,并成為一個(gè)更快樂、更富有成效的程序員。2015-10-10layui動(dòng)態(tài)加載多表頭的實(shí)例
今天小編就為大家分享一篇layui動(dòng)態(tài)加載多表頭的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-09-09