javascript簡單實(shí)現(xiàn)深淺拷貝過程詳解
前言
深淺拷貝知識(shí)在我們的日常開發(fā)中還算是用的比較多,但是之前的狀態(tài)一直都是只曾聽聞,未曾使用(其實(shí)用了只是自己沒有意識(shí)到),所以今天來跟大家聊一聊js的深淺拷貝;
首先我們來了解一下javascript的數(shù)據(jù)類型,在ES5版本的js中我們的javascript一共有6種數(shù)據(jù)類型,分別是:
Number(數(shù)值型)、String(字符串)、Boolean(布爾型)、Object(對(duì)象,object和array都屬于Object類型)、null、undefined
我們?nèi)粘J褂玫膉avascript深淺拷貝主要是面向Object引用類型進(jìn)行拷貝;
我們知道了js的深淺拷貝面對(duì)的執(zhí)行操作對(duì)象,然后我們?cè)賮砜匆幌律顪\拷貝的概念:
拷貝顧名思義就是復(fù)制,內(nèi)存中一共分為棧內(nèi)存和堆內(nèi)存兩大區(qū)域,所謂深淺拷貝主要是對(duì)javascript引用類型數(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)行存放;
基本類型復(fù)制:
var a = 1; var b = a;//復(fù)制 console.log(b)//1 a = 2;//改變a的值 console.log(b)//1 console.log(a) //2
因?yàn)閍,b都是屬于基本類型,基本類型的復(fù)制是不會(huì)影響對(duì)方的,因?yàn)榛绢愋褪敲恳淮蝿?chuàng)建變量都會(huì)在棧內(nèi)存中開辟一塊內(nèi)存,用來存放值,所以基本類型進(jìn)行復(fù)制是不會(huì)對(duì)另外一個(gè)變量有影響的;
引用類型復(fù)制:
引用類型的復(fù)制我們分為數(shù)組的復(fù)制和對(duì)象的復(fù)制兩個(gè)方面來進(jìn)行講解:
js的淺拷貝:
var arr1 = ['red','green']; var arr2 = arr1;//復(fù)制 console.log(arr2)//['red','green']; arr1.push('black') ;//改變color1的值 console.log(arr2)//['red','green','black'] console.log(arr1) //["red", "green", "black"]
上面的案例是javascript數(shù)組的淺拷貝,通過上面的知識(shí)我們可以看知道數(shù)組是引用類型數(shù)據(jù),引用類型數(shù)據(jù)復(fù)制是會(huì)進(jìn)行相互影響的,我們看到arr1.push('black')添加了一個(gè)新的子項(xiàng),因?yàn)樯厦鎣ar arr2=arr1這行代碼是將兩個(gè)引用類型數(shù)據(jù)的地址指針指向了同一塊堆內(nèi)存區(qū)域,所以不管是arr1還是arr2修改,任何一個(gè)一個(gè)改動(dòng)兩個(gè)數(shù)組都是會(huì)互相產(chǎn)生影響的;上面的那種直接賦值方式的復(fù)制就是我們常說的引用類型的淺拷貝;
關(guān)于深拷貝很多同學(xué)都誤以為js的原生方法concat、slice是屬于深拷貝,其實(shí)不是的;js的原生方法concat、slice都是僅適用于一維數(shù)組,一旦到了二維數(shù)組或者多維數(shù)組中就會(huì)出現(xiàn)問題,就出現(xiàn)拷貝的不夠徹底導(dǎo)致還是會(huì)發(fā)生數(shù)據(jù)的相互牽引問題;
slice:
var arr1 = ['red','green']; var arr2 = arr1.slice(0);//復(fù)制 console.log(arr2)//['red','green']; arr1.push('black') ;//改變color1的值 console.log(arr2)//["red", "green"] console.log(arr1)//["red", "green", "black"]
js原生的方法slice會(huì)返回一個(gè)新的數(shù)組,上述代碼乍一看會(huì)以為是深拷貝,因?yàn)閍rr2和arr1相互復(fù)制和牽引,而當(dāng)arr1調(diào)用了push方法添加了新數(shù)組子項(xiàng)的時(shí)候,arr2沒有發(fā)生變化;是的,這是符合深拷貝的特性,但是拷貝的不夠徹底,所以還不能算是真正意義上的深拷貝,所以slice只能被稱為淺拷貝;slice方法只適用于一維數(shù)組的拷貝,在二維數(shù)組中就會(huì)破綻百出;
下面我們?cè)賮砜匆幌露S數(shù)組的例子:
var arr1=[1,2,3,['1','2','3']]; var arr2=arr1.slice(0); arr1[3][0]=0; console.log(arr1);//[1,2,3,['0','2','3']] console.log(arr2);//[1,2,3,['0','2','3']]
上述代碼是一個(gè)二維數(shù)組,當(dāng)我們?cè)赼rr1[3][0]里面進(jìn)行更改arr1的值的時(shí)候,我們發(fā)現(xiàn)arr1、arr2兩個(gè)數(shù)組的值都發(fā)生了變化;所以事實(shí)證明slice不是深拷貝;
concat:
var arr1 = ['red','green']; var arr2 = arr1.concat();//復(fù)制 console.log(arr2)//['red','green']; arr1.push('black') ;//改變color1的值 console.log(arr2)//["red", "green"] console.log(arr1)//["red", "green", "black"]
var arr1=[1,2,3,['1','2','3']]; var arr2=arr1.concat(); arr1[3][0]=0; console.log(arr1);//[1,2,3,['0','2','3']] console.log(arr2);//[1,2,3,['0','2','3']]
concat方法在一維數(shù)組中是不會(huì)影響源數(shù)組的數(shù)據(jù)的,而在二維數(shù)組中concat的表現(xiàn)和slice是一樣的;
js的深拷貝:
js數(shù)組中實(shí)現(xiàn)深拷貝的方法都多種,比如JSON.parse(JSON.stringify())和遞歸以及JQuery庫的extend方法(只是extend方法需要依賴JQuery庫,所以我們盡量的使用原生的方式來實(shí)現(xiàn))都是可以實(shí)現(xiàn)數(shù)組和對(duì)象的深拷貝的;
var arr1 = ['red','green']; var arr2 = JSON.parse(JSON.stringify(arr1));//復(fù)制 console.log(arr2)//['red','green']; arr1.push('black') ;//改變color1的值 console.log(arr2)//["red", "green"] console.log(arr1)//["red", "green", "black"]
上述代碼中我們可以清晰的看到JSON.parse(JSON.stringify())是真正意義上實(shí)現(xiàn)了深拷貝;
遞歸實(shí)現(xiàn)深拷貝:
function deepClone(obj){ //判斷參數(shù)是不是一個(gè)對(duì)象 let objClone = obj instanceof Object?[]:{}; if(obj && typeof obj==="object"){ for(key in obj){ if(obj.hasOwnProperty(key)){ //判斷ojb子元素是否為對(duì)象,如果是,遞歸復(fù)制 if(obj[key]&&typeof obj[key] ==="object"){ objClone[key] = deepClone(obj[key]); }else{ //如果不是,簡單復(fù)制 objClone[key] = obj[key]; } } } } return objClone; } var a ={ x:1, y:2 }; b=deepClone(a); a.x=3 console.log(a); console.log(b);
輸出效果如下:
總結(jié):
1:深拷貝只是從源數(shù)據(jù)中拷貝一份出來進(jìn)行操作,而不是改變?cè)磾?shù)據(jù);改變?cè)磾?shù)據(jù)的那是淺拷貝;
2:原生js方法slice、concat都不是真正意義上的深拷貝,都僅只適用于一維數(shù)組,拷貝的屬性不夠徹底;
3:實(shí)現(xiàn)js深拷貝我們可以通過JSON.parse(JSON.stringify())、遞歸以及JQuery庫的extend方法來實(shí)現(xiàn);
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
用js實(shí)現(xiàn)放大鏡的效果的簡單實(shí)例
下面小編就為大家?guī)硪黄胘s實(shí)現(xiàn)放大鏡的效果的簡單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-05-05js超時(shí)調(diào)用setTimeout和間歇調(diào)用setInterval實(shí)例分析
這篇文章主要介紹了js超時(shí)調(diào)用setTimeout和間歇調(diào)用setInterval,以實(shí)例形式對(duì)比分析了setTimeout與setInterval的具體使用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-01-01使用JavaScript實(shí)現(xiàn)一個(gè)拖拽縮放效果
這篇文章主要介紹了如何使用JS實(shí)現(xiàn)一個(gè)這樣的拖拽縮放效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05layui中的tab控件點(diǎn)擊切換觸發(fā)事件
這篇文章主要介紹了layui中的tab控件點(diǎn)擊切換觸發(fā)事件,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06點(diǎn)擊按鈕或鏈接不跳轉(zhuǎn)只刷新頁面的腳本整理
點(diǎn)擊按鈕或鏈接時(shí)不跳轉(zhuǎn)只刷新頁面,在某些情況下還是比較實(shí)用的,下面整理些不錯(cuò)的示例,感興趣的朋友可以參考下2013-10-10Javascript實(shí)現(xiàn)禁止輸入中文或英文的例子
這篇文章主要介紹了Javascript實(shí)現(xiàn)禁止輸入中文或英文的方法實(shí)例,本文方法都是使用正則表達(dá)式實(shí)現(xiàn),需要的朋友可以參考下2014-12-12javascript實(shí)現(xiàn)抽獎(jiǎng)程序的簡單實(shí)例
下面小編就為大家?guī)硪黄猨avascript實(shí)現(xiàn)抽獎(jiǎng)程序的簡單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06原生js實(shí)現(xiàn)簡易抽獎(jiǎng)系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了原生js實(shí)現(xiàn)簡易抽獎(jiǎng)系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03