本文出自【聽風是風】
在項目中遇到一個頁面中保存的數(shù)組及對象在下個頁面需要調用以及返回時第一個頁面還需要用第一次生成的數(shù)據(jù),就使用到了深拷貝? ? 就對其進行了研究
如何區(qū)分深拷貝與淺拷貝侨舆,簡單點來說升酣,就是假設B復制了A,當修改A時态罪,看B是否會發(fā)生變化,如果B也跟著變了下面,說明這是淺拷貝复颈,拿人手短,如果B沒變,那就是深拷貝耗啦,自食其力凿菩。【講的通俗易懂】
我們來舉個淺拷貝例子:
let a=[0,1,2,3,4],
b=a;
console.log(a===b);
a[0]=1;
console.log(a,b);
明明b復制了a帜讲,為啥修改數(shù)組a衅谷,數(shù)組b也跟著變了
那么這里,就得引入基本數(shù)據(jù)類型與引用數(shù)據(jù)類型的概念了似将。
面試常問获黔,基本數(shù)據(jù)類型有哪些,number在验,string玷氏,boolean,null腋舌,undefined盏触,symbol以及未來ES10新增的BigInt(任意精度整數(shù))七類。
引用數(shù)據(jù)類型(Object類)有常規(guī)名值對的無序對象{a:1}块饺,數(shù)組[1,2,3]赞辩,以及函數(shù)等。
而這兩類數(shù)據(jù)存儲分別是這樣的:
a.基本類型--名值存儲在棧內(nèi)存中授艰,例如let a=1;
? ??????????
當你b=a復制時辨嗽,棧內(nèi)存會新開辟一個內(nèi)存,例如這樣:
所以當你此時修改a=2想诅,對b并不會造成影響召庞,因為此時的b已自食其力,翅膀硬了来破,不受a的影響了篮灼。當然,let a=1,b=a;雖然b不受a影響徘禁,但這也算不上深拷貝诅诱,因為深拷貝本身只針對較為復雜的object類型數(shù)據(jù)。
b.引用數(shù)據(jù)類型--名存在棧內(nèi)存中送朱,值存在于堆內(nèi)存中娘荡,但是棧內(nèi)存會提供一個引用的地址指向堆內(nèi)存中的值,我們以上面淺拷貝的例子畫個圖:
當b=a進行拷貝時驶沼,其實復制的是a的引用地址炮沐,而并非堆里面的值。
而當我們a[0]=1時進行數(shù)組修改時回怜,由于a與b指向的是同一個地址大年,所以自然b也受了影響,這就是所謂的淺拷貝了。
那翔试,要是在堆內(nèi)存中也開辟一個新的內(nèi)存專門為b存放值轻要,就像基本類型那樣,豈不就達到深拷貝的效果了
1.我們怎么去實現(xiàn)深拷貝呢垦缅,這里可以遞歸遞歸去復制所有層級屬性冲泥。
這么我們封裝一個深拷貝的函數(shù)(PS:只是一個基本實現(xiàn)的展示,并非最佳實踐)
function deepClone(obj){
? ? let objClone = Array.isArray(obj)?[]:{};
? ? if(obj &&typeofobj==="object"){
? ? ? ? for(keyin obj){
? ? ? ? ? ? if(obj.hasOwnProperty(key)){
? ? ? ? ? ? ? ? //判斷ojb子元素是否為對象壁涎,如果是凡恍,遞歸復制if(obj[key]&&typeofobj[key] ==="object"){
? ? ? ? ? ? ? ? ? ? objClone[key] = deepClone(obj[key]);
? ? ? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? ? ? //如果不是,簡單復制objClone[key] = obj[key];
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? return objClone;
}? ?
let a=[1,2,3,4],
? ? b=deepClone(a);
a[0]=2;
console.log(a,b);
可以看到
跟之前想象的一樣粹庞,現(xiàn)在b脫離了a的控制咳焚,不再受a影響了。
這里再次強調庞溜,深拷貝革半,是拷貝對象各個層級的屬性,可以看個例子流码。JQ里有一個extend方法也可以拷貝對象又官,我們來看看
let a=[1,2,3,4],
? ? b=a.slice();
a[0]=2;
console.log(a,b);
那是不是說slice方法也是深拷貝了,畢竟b也沒受a的影響漫试,上面說了六敬,深拷貝是會拷貝所有層級的屬性,還是這個例子驾荣,我們把a改改
let a=[0,1,[2,3],4],
? ? ? ? b=a.slice();
a[0]=1;a[
2][0]=1;
console.log(a,b);
拷貝的不徹底啊外构,b對象的一級屬性確實不受影響了,但是二級屬性還是沒能拷貝成功播掷,仍然脫離不了a的控制审编,說明slice根本不是真正的深拷貝。
第一層的屬性確實深拷貝歧匈,擁有了獨立的內(nèi)存垒酬,但更深的屬性卻仍然公用了地址,所以才會造成上面的問題件炉。
同理勘究,concat方法與slice也存在這樣的情況,他們都不是真正的深拷貝斟冕,這里需要注意口糕。
2.除了遞歸,我們還可以借用JSON對象的parse和stringify
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);
可以看到磕蛇,這下b是完全不受a的影響了走净。
附帶說下券时,JSON.stringify與JSON.parse除了實現(xiàn)深拷貝,還能結合localStorage實現(xiàn)對象數(shù)組存儲
3.除了上面兩種方法之外伏伯,我們還可以借用JQ的extend方法。
$.extend( [deep ], target, object1 [, objectN ] )
deep表示是否深拷貝捌袜,為true為深拷貝说搅,為false,則為淺拷貝
target?Object類型 目標對象虏等,其他對象的成員屬性將被附加到該對象上弄唧。
object1??objectN可選。 Object類型 第一個以及第N個被合并的對象霍衫。?
哈哈哈哈? 反正我明白了候引。。敦跌。澄干。