JavaScript最完整的深淺拷貝實現(xiàn)方式詳解
深淺拷貝:
內存中一共分為棧內存和堆內存兩大區(qū)域,所謂深淺拷貝主要是對js引用類型數(shù)據(jù)進行拷貝一份,淺拷貝就是引用類型數(shù)據(jù)相互賦值之后,例obj1=obj2;如果后面的操作中修改obj1或者obj2,這個時候數(shù)據(jù)是會進行相應的變化的,因為在內存中引用類型數(shù)據(jù)是存儲在堆內存中,堆內存中存放的是引用類型的值,同時會有一個指針地址指向棧內存,兩個引用類型數(shù)據(jù)地址一樣,如果其中一個發(fā)生變化另外一個都會有影響;而深拷貝則不會,深拷貝是會在堆內存中重新開辟一塊空間進行存放;
簡單來說就是B復制了A,如果A發(fā)生了改變,如果B隨之變化,那么是淺拷貝,如果B并沒有發(fā)生變化,則是深拷貝。
基本類型拷貝
let a = 1; let b = a; b = 2; console.log(a);//1 console.log(b);//2
a,b都是屬于基本類型,基本類型的復制是不會影響對方的,因為基本類型是每一次創(chuàng)建變量都會在棧內存中開辟一塊內存,用來存放值,所以對基本類型進行拷貝是不會對另外一個變量有影響的,屬于深拷貝。
數(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']上面兩種方法只能實現(xiàn)數(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ā)生了變化,說明沒有實現(xiàn)深拷貝。
由此總結,concat和slice只能實現(xiàn)一維數(shù)組的深拷貝,不能對多維數(shù)組進行深拷貝,所以并不是一個完美的深拷貝方式。
對象拷貝
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隨之更改了,因此其實并不是深拷貝。
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);
以上兩種方法,對于一層對象都能實現(xiàn)深拷貝,但對于多層對象則無法實現(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);
單層對象name和多層對象obj.id的改變都沒影響到原對象,因此是一個比較完美的深拷貝,但是能看到function好像并沒有被拷貝。
可以看出這個方法對于一層和多層都能實現(xiàn)深拷貝,但是這個方法的缺陷是不能拷貝Function,所以在使用時,一定要注意數(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'){ //判斷是不是對象(數(shù)組或對象)
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);
可以看到,無論是多層的對象還是多層的數(shù)組,都能實現(xiàn)深拷貝,而且能拷貝函數(shù),這個是目前來說最完美的深拷貝方法。
通過這個遞歸能實現(xià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);
這個方法能實現(xiàn)對象數(shù)組單層時的深拷貝,但是多層無法實現(xiàn)深拷貝。
以上這么多方法,最優(yōu)解應該屬于JSON和遞歸的方法(遞歸解決了JSON無法拷貝函數(shù)方法的問題),但是根據(jù)需求數(shù)據(jù)類型的不同,可以選擇更簡單的方式。
總結
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注腳本之家的更多內容!
相關文章
javaScript嗅探執(zhí)行神器-sniffer.js
本文主要介紹了javaScript嗅探執(zhí)行神器-sniffer.js的相關知識。具有很好的參考價值,下面跟著小編一起來看下吧2017-02-02
JavaScript?Generator異步過度的實現(xiàn)詳解
生成器Generator是JavaScript?ES6引入的特性,它讓我們可以分段執(zhí)行一個函數(shù)。但是在談論生成器(Generator)之前,我們要先了解迭代器Iterator2022-08-08
js 實現(xiàn) input type="file" 文件上傳示例代碼
在開發(fā)中,文件上傳必不可少但是它長得又丑、瀏覽的字樣不能換,一般會讓其隱藏點其他的標簽(圖片等)來時實現(xiàn)選擇文件上傳功能2013-08-08
JavaScript?Promise多并發(fā)問題的解決方法詳解
提起控制并發(fā),大家應該不陌生,這篇文章主要來和大家介紹一下JavaScript如何解決Promise多并發(fā)問題,感興趣的小伙伴可以跟隨小編一起學習一下2023-09-09

