我是前端小白一枚,為了鞏固知識和增強記憶,開始整理相關的知識,方便以後複習和麵試的時候看看。OK,讓我們進入正題~ 先說說淺拷貝和深拷貝的理解吧,個人是這樣理解的:兩個對象A、B, A有數據B為空,B複製了A,我們修改A,如果B中的數據跟著變化了,那就是淺拷貝,如果沒有變化,那就是深拷貝。說明B另開 ...
我是前端小白一枚,為了鞏固知識和增強記憶,開始整理相關的知識,方便以後複習和麵試的時候看看。
OK,讓我們進入正題~
先說說淺拷貝和深拷貝的理解吧,個人是這樣理解的:
兩個對象A、B, A有數據B為空,B複製了A,我們修改A,如果B中的數據跟著變化了,那就是淺拷貝,如果沒有變化,那就是深拷貝。說明B另開闢了一塊記憶體(下文會講解)。
舉個慄子:
AB兩個對象,我只改變了A中的元素,為什麼B中的元素也跟著變化了?(上面是一個淺拷貝的慄子)
要深入理解淺拷貝和深拷貝的原理,那就要涉及到一些基本數據類型和引用數據類型的知識了;
基本數據類型:number,string,boolean,null,undefined,symbol等;
引用數據類型:object({}對象,數組[]),function函數等;
因為拷貝是對數據進行操作,所以我們得瞭解一下這兩類的數據存儲方式;
基本數據類型一般存儲在棧中;引用數據類型一般存放在堆中; 話不多說上圖!
a.基本類型--屬性名,屬性值存儲在棧記憶體中,例如var a=1;
當你b=a複製時,棧記憶體會新開闢一個記憶體,例如這樣:
當我們修改a = 2時,b中的數據沒有改變;
我們可以得出,基本數據類型中,複製一個對象,會在棧中開闢一塊新記憶體,改變其中一個對象另一個對象不會受影響;但是這不是深拷貝,深拷貝只針對較為複雜的object數據類型;
b.引用數據類型--屬性名存在棧記憶體中,屬性值存在於堆記憶體中,但是棧記憶體會提供一個引用的地址指向堆記憶體中的值,我們以上面淺拷貝的例子畫個圖:
當b=a進行拷貝時,其實複製的是a的引用地址,而並非堆裡面的值。
而當我們a[0]=1時進行數組修改時,由於a與b指向的是同一個地址,所以自然b也受了影響,這就是所謂的淺拷貝了。
那,要是在堆記憶體中也開闢一個新的記憶體專門為b存放值,就像基本類型那樣,豈不就達到深拷貝的效果了
OK,瞭解了什麼是淺拷貝和深拷貝後,那怎麼實現深拷貝呢?
深拷貝的實現:
1、利用遞歸的方式,遞歸所有層級的屬性;
列印出來的結果顯示,改變a,b沒有改變;
2、利用json.stringify()和json.parse();
function deepClone(obj){ let _obj = JSON.stringify(obj), objClone = JSON.parse(_obj); return objClone } let a=[0,1,[2,3],4], b=deepClone(a); a[0]=1; a[2][0]=1; console.log(a,b);
3、借用JQ的extend方法
$.extend( [deep ], target, object1 [, objectN ] )
deep表示是否深拷貝,為true為深拷貝,為false,則為淺拷貝
target Object類型 目標對象,其他對象的成員屬性將被附加到該對象上。
object1 objectN可選。 Object類型 第一個以及第N個被合併的對象。
let a=[0,1,[2,3],4], b=$.extend(true,[],a); a[0]=1; a[2][0]=1; console.log(a,b);
講了這麼多,我們來說下用途,我們在開發中,如果有很多數據要處理,貿然的對數據進行增刪改的話,可能會存在污染源數據問題,導致數據無法恢復;
深拷貝就幫助我們解決了這個隱患,我們就可以安心的去對數據進行相應的操作;
註:有些方法也可以實現拷貝,例如:slice()、assign()等方法,這兩個方式如果拷貝的層級只有一層為深拷貝,如果拷貝的層級是多層,那就是淺拷貝;
這是我的第一篇博客,以後會慢慢寫一些東西發表一下,一來是可以用於面試複習,二來又可以對這些知識複習,可以加強記憶;
如果有寫的不好的地方或者有問題的地方,歡迎大家一起討論;