我們都知道,JavaScript中有兩種數(shù)據(jù)類型,基本數(shù)據(jù)類型和引用數(shù)據(jù)類型。
對(duì)于js中的基本數(shù)據(jù)類型,如number、string、boolean等,我們都是按值訪問(wèn),因此淺拷貝是對(duì)其值的拷貝;對(duì)于引用類型,我們都是按引用訪問(wèn),即保存在變量對(duì)象中的地址。通過(guò)該地址去訪問(wèn)堆內(nèi)存里的實(shí)際值。因此,對(duì)于相對(duì)復(fù)雜的object類型的數(shù)據(jù),如對(duì)象、數(shù)組等就存在淺拷貝和深拷貝。
淺拷貝是對(duì)對(duì)象地址的拷貝,并沒(méi)有開辟新的內(nèi)存空間,即復(fù)制的結(jié)果會(huì)是兩個(gè)對(duì)象指向同一個(gè)地址,修改其中一個(gè)的對(duì)象屬性,另一個(gè)對(duì)象屬性也會(huì)發(fā)生改變。
var a = 10;
var b = a;
b = 100;
console.log(a); //10
console.log(b); //100
//對(duì)象的淺拷貝
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = obj1;
obj2.b = 200;
console.log(obj1); //{a: 10, b: 200, c: 30}
console.log(obj2); //{a: 10, b: 200, c: 30}
深拷貝則會(huì)開辟新的內(nèi)存空間,兩個(gè)對(duì)象對(duì)應(yīng)不同的地址,修改一個(gè)對(duì)象的屬性,并不會(huì)改變另一個(gè)對(duì)象的屬性。如下代碼所示:
//深拷貝
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
obj2.b = 200;
console.log(obj1); // { a: 10, b: 20, c: 30 }
console.log(obj2); // { a: 10, b: 200, c: 30 }
一.淺拷貝的實(shí)現(xiàn):
我們?cè)O(shè)想一下如何將一個(gè)對(duì)象里的屬性和方法復(fù)制到另一個(gè)對(duì)象里呢?
var person = { name: 'Mary', age: '20'};
var teacher = {sex: 'man'};
function shallowCopy( obj, obj2 ){
var obj2 = obj2 || {};
for( var item in obj ){
obj2[item] = obj[item]
}
return obj2;
}
shallowCopy( person, teacher );
console.log( person );
console.log( teacher );
但我們?nèi)绻麑⒋a做如下修改,person對(duì)象里面還有一個(gè)子對(duì)象grade,則在復(fù)制時(shí)淺拷貝只是將子對(duì)象的引用傳遞給新的對(duì)象,因此改變?nèi)我粋€(gè)的grade屬性都會(huì)影響到另一個(gè)。
2.使用Object.assign() 方法可以把任意多個(gè)的源對(duì)象自身的可枚舉屬性拷貝給目標(biāo)對(duì)象,然后返回目標(biāo)對(duì)象。如果在這個(gè)過(guò)程中出現(xiàn)同名的屬性(方法),后合并的屬性(方法)會(huì)覆蓋之前的同名屬性(方法)。
Object.assign(target,source1,source2,source3);
但是 Object.assign() 進(jìn)行的也是淺拷貝,拷貝的是對(duì)象的屬性的引用,而不是對(duì)象本身。
var obj = {a: {b: 1}};
var shallowClone= Object.assign({}, obj);
obj2.a.b = 2;
obj.a.b //2
二.深拷貝的實(shí)現(xiàn)
我們需要深拷貝來(lái) 解決引用類型的拷貝問(wèn)題,常見的深拷貝方法有:
1.采用遞歸的方法去拷貝對(duì)象:
ar person = { name: 'Mary', age: '20',grade: {English: 100} };
var teacher = {sex: 'man'};
function deepCopy( obj, obj2 ){
var obj2 = obj2 || {};
for( var item in obj ){
if( typeof obj[item] == 'object'){
obj2[item] = ( obj[item].constructor === Array )?[]:{};
deepCopy( obj[item], obj2[item] )
}else{
obj2[item] = obj[item];
}
}
return obj2;
}
deepCopy( person, teacher );
teacher.name = 'Tom';
teacher.grade.English = 120;
console.log( person );
console.log( teacher );
2.使用JSON方法:
用JSON.stringify把對(duì)象轉(zhuǎn)成字符串,再用JSON.parse把字符串轉(zhuǎn)成新的對(duì)象。
這種方法雖然簡(jiǎn)單,但它會(huì)拋棄對(duì)象的constructor,也就是深拷貝之后,不管這個(gè)對(duì)象原來(lái)的構(gòu)造函數(shù)是什么,在深拷貝之后都會(huì)變成Object。 并且只能正確處理那些能夠被 json 直接表示的數(shù)據(jù)結(jié)構(gòu)。RegExp對(duì)象、function都是無(wú)法通過(guò)這種方式深拷貝的。
var obj = { grade: { English: 100 }};
var obj2 = JSON.parse( JSON.stringify( obj ));
obj2.grade.English = 110;
console.log( obj.grade.English); //100
console.log( obj2.grade.English); //110
3.使用Object.create()方法:
Object.create(prototype, descriptors)
參數(shù)說(shuō)明:
prototype:必需。 要用作原型的對(duì)象。 可以為 null。
descriptors“”可選。 包含一個(gè)或多個(gè)屬性描述符的 JavaScript 對(duì)象。 “數(shù)據(jù)屬性”是可獲取且可設(shè)置值的屬性。 數(shù)據(jù)屬性描述符包含 value 特性,以及 writable、enumerable 和 configurable 特性。 如果未指定最后三個(gè)特性,則它們默認(rèn)為 false。 只要檢索或設(shè)置該值,“訪問(wèn)器屬性”就會(huì)調(diào)用用戶提供的函數(shù)。 訪問(wèn)器屬性描述符包含 set 特性和/或 get 特性。
返回值:一個(gè)具有指定的內(nèi)部原型且包含指定的屬性(如果有)的新對(duì)象。
個(gè)人前端學(xué)習(xí)筆記均為原創(chuàng)。
首發(fā)CSDN:Freya_yyy的博客。歡迎交流和指導(dǎo)。
我是木風(fēng),愿你遇見美好!