徹底搞懂深拷貝與淺拷貝
深拷貝淺拷貝只是針對引用資料型別
JavaScript資料型別包括基本資料型別(Number,String,boolean,Null,Undefined,Symbol)、引用資料型別(Array,Object)
-
基本資料型別的特點:直接儲存在棧(stack)中的資料
-
引用資料類在棧中存有指標,實體資料存在堆中,通過棧中的指標找到堆中的引用資料型別的實體
堆和棧的區別
淺拷貝只複製指向某個物件的指標,而不復制物件本身,新舊物件還是共享同一塊記憶體。但深拷貝會另外創造一個一模一樣的物件,新物件跟原物件不共享記憶體,修改新物件不會改到原物件。
賦值和淺拷貝的區別
當我們把一個物件賦值給一個新的變數時,賦的其實是該物件的在棧中的地址,而不是堆中的資料。
淺拷貝是按位拷貝物件,它會建立一個新物件,這個物件有著原始物件屬性值的一份精確拷貝。如果屬性是基本型別,拷貝的就是基本型別的值;如果屬性是記憶體地址(引用型別),拷貝的就是記憶體地址 ,因此如果其中一個物件改變了這個地址,就會影響到另一個物件。即預設拷貝建構函式只是對物件進行淺拷貝複製(逐個成員依次拷貝),即只複製物件空間而不復制資源。
// 物件賦值 var obj1 = { 'name' : 'zhangsan', 'age' : '18', 'language' : [1,[2,3],[4,5]], }; var obj2 = obj1; obj2.name = "lisi"; obj2.language[1] = ["二","三"]; console.log('obj1',obj1) console.log('obj2',obj2)
// 淺拷貝 var obj1 = { 'name' : 'zhangsan', 'age' : '18', 'language' : [1,[2,3],[4,5]], }; var obj3 = shallowCopy(obj1); obj3.name = "lisi"; obj3.language[1] = ["二","三"]; function shallowCopy(src) { var dst = {}; for (var prop in src) { if (src.hasOwnProperty(prop)) { dst[prop] = src[prop]; } } return dst; } console.log('obj1',obj1) console.log('obj3',obj3)
上面例子中,obj1是原始資料,obj2是賦值操作得到,而obj3淺拷貝得到。我們可以很清晰看到對原始資料的影響,具體請看下錶:
淺拷貝的實現方法
1. Object.assign()
Object.assign() 方法可以把任意多個的源物件自身的可列舉屬性拷貝給目標物件,然後返回目標物件。但是 Object.assign() 進行的是淺拷貝,拷貝的是物件的屬性的引用,而不是物件本身。
對於Object.assign()而言,如果物件的屬性值為簡單型別(string,number),通過Object.assign({},srcobj);得到的新物件為深拷貝;如果屬性值為物件或其他引用型別,那對於這個物件而言其實是淺拷貝
2. Array.prototype.concat()
let arr = [1, 3, {
username: 'kobe'
}];
let arr2=arr.concat();
arr2[2].username = 'wade';
console.log(arr);
3. Array.prototype.slice()
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);
深拷貝的實現
1. JSON.parse(JSON.stringify())
原理: 用JSON.stringify將物件轉成JSON字串,再用JSON.parse()把字串解析成物件,一去一來,新的物件產生了,而且物件會開闢新的棧,實現深拷貝。
這種方法雖然可以實現陣列或物件深拷貝,但不能處理函式。
這是因為 JSON.stringify() 方法是將一個JavaScript值(物件或者陣列)轉換為一個 JSON字串,不能接受函式。
2. 手寫遞迴方法
//遞迴來實現深拷貝
function deepClone(obj){
var newObj = obj instanceof Array ? []:{};
if(typeof obj !== 'object'){
return obj;
}else {
for(var i in obj) {
newObj[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i];
}
}
return newObj;
}
var q = [1, 2, 4, 6, { age: 12 }, [1, 2]];
var d = deepClone(q)
console.log(d)
3. 函式庫lodash
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false