深淺拷貝知識在咱們的平常開發中還算是用的比較多,可是以前的狀態一直都是隻曾聽聞,不曾使用(其實用了只是本身沒有意識到),因此今天來跟你們聊一聊js的深淺拷貝;
javascript
首先咱們來了解一下javascript的數據類型,在ES5版本的js中咱們的javascript一共有6種數據類型,分別是:java
Number(數值型)、String(字符串)、Boolean(布爾型)、Object(對象,object和array都屬於Object類型)、null、undefined數組
咱們平常使用的javascript深淺拷貝主要是面向Object引用類型進行拷貝;spa
咱們知道了js的深淺拷貝面對的執行操做對象,而後咱們再來看一下深淺拷貝的概念:指針
拷貝顧名思義就是複製,內存中一共分爲棧內存和堆內存兩大區域,所謂深淺拷貝主要是對javascript引用類型數據進行拷貝一份,淺拷貝就是引用類型數據相互賦值以後,例obj1=obj2;若是後面的操做中修改obj1或者obj2,這個時候數據是會進行相應的變化的,由於在內存中引用類型數據是存儲在堆內存中,堆內存中存放的是引用類型的值,同時會有一個指針地址指向棧內存,兩個引用類型數據地址同樣,若是其中一個發生變化另一個都會有影響;而深拷貝則不會,深拷貝是會在堆內存中從新開闢一塊空間進行存放;code
基本類型複製:對象
var a = 1; var b = a;//複製 console.log(b)//1 a = 2;//改變a的值 console.log(b)//1 console.log(a) //2
由於a,b都是屬於基本類型,基本類型的複製是不會影響對方的,由於基本類型是每一次建立變量都會在棧內存中開闢一塊內存,用來存放值,因此基本類型進行復制是不會對另一個變量有影響的;blog
引用類型複製:遞歸
引用類型的複製咱們分爲數組的複製和對象的複製兩個方面來進行講解:ip
js的淺拷貝:
var arr1 = ['red','green']; var arr2 = arr1;//複製 console.log(arr2)//['red','green']; arr1.push('black') ;//改變color1的值 console.log(arr2)//['red','green','black'] console.log(arr1) //["red", "green", "black"]
上面的案例是javascript數組的淺拷貝,經過上面的知識咱們能夠看知道數組是引用類型數據,引用類型數據複製是會進行相互影響的,咱們看到arr1.push('black')添加了一個新的子項,由於上面var arr2=arr1這行代碼是將兩個引用類型數據的地址指針指向了同一塊堆內存區域,因此無論是arr1仍是arr2修改,任何一個一個改動兩個數組都是會互相產生影響的;上面的那種直接賦值方式的複製就是咱們常說的引用類型的淺拷貝;
關於深拷貝不少同窗都誤覺得js的原生方法concat、slice是屬於深拷貝,其實不是的;js的原生方法concat、slice都是僅適用於一維數組,一旦到了二維數組或者多維數組中就會出現問題,就出現拷貝的不夠完全致使仍是會發生數據的相互牽引問題;
slice:
var arr1 = ['red','green']; var arr2 = arr1.slice(0);//複製 console.log(arr2)//['red','green']; arr1.push('black') ;//改變color1的值 console.log(arr2)//["red", "green"] console.log(arr1)//["red", "green", "black"]
js原生的方法slice會返回一個新的數組,上述代碼乍一看會覺得是深拷貝,由於arr2和arr1相互複製和牽引,而當arr1調用了push方法添加了新數組子項的時候,arr2沒有發生變化;是的,這是符合深拷貝的特性,可是拷貝的不夠完全,因此還不能算是真正意義上的深拷貝,因此slice只能被稱爲淺拷貝;slice方法只適用於一維數組的拷貝,在二維數組中就會破綻百出;
下面咱們再來看一下二維數組的例子:
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']]
上述代碼是一個二維數組,當咱們在arr1[3][0]裏面進行更改arr1的值的時候,咱們發現arr一、arr2兩個數組的值都發生了變化;因此事實證實slice不是深拷貝;
concat:
var arr1 = ['red','green']; var arr2 = arr1.concat();//複製 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方法在一維數組中是不會影響源數組的數據的,而在二維數組中concat的表現和slice是同樣的;
js的深拷貝:
js數組中實現深拷貝的方法都多種,好比JSON.parse(JSON.stringify())和遞歸以及JQuery庫的extend方法(只是extend方法須要依賴JQuery庫,因此咱們儘可能的使用原生的方式來實現)都是能夠實現數組和對象的深拷貝的;
var arr1 = ['red','green']; var arr2 = JSON.parse(JSON.stringify(arr1));//複製 console.log(arr2)//['red','green']; arr1.push('black') ;//改變color1的值 console.log(arr2)//["red", "green"] console.log(arr1)//["red", "green", "black"]
上述代碼中咱們能夠清晰的看到JSON.parse(JSON.stringify())是真正意義上實現了深拷貝;
遞歸實現深拷貝:
function deepClone(obj){ //判斷參數是否是一個對象 let objClone = obj instanceof Object?[]:{}; if(obj && typeof obj==="object"){ for(key in obj){ if(obj.hasOwnProperty(key)){ //判斷ojb子元素是否爲對象,若是是,遞歸複製 if(obj[key]&&typeof obj[key] ==="object"){ objClone[key] = deepClone(obj[key]); }else{ //若是不是,簡單複製 objClone[key] = obj[key]; } } } } return objClone; } var a ={ x:1, y:2 }; b=deepClone(a); a.x=3 console.log(a); console.log(b);
輸出效果以下:
總結:
1:深拷貝只是從源數據中拷貝一份出來進行操做,而不是改變源數據;改變源數據的那是淺拷貝;
2:原生js方法slice、concat都不是真正意義上的深拷貝,都僅只適用於一維數組,拷貝的屬性不夠完全;
3:實現js深拷貝咱們能夠經過JSON.parse(JSON.stringify())、遞歸以及JQuery庫的extend方法來實現;