賦值,淺拷貝,深拷貝的區別

一、賦值(Copy)

賦值是將某一數值或對象賦給某個變量的過程,分爲下面 2 部分:

基本數據類型:賦值,賦值之後兩個變量互不影響
引用數據類型:賦址,兩個變量具有相同的引用,指向同一個對象,相互之間有影響

let a = 10;
let b = a;
console.log(a);//10
console.log(b);//100
a = 100;
console.log(a);//100
console.log(b);//10

對基本類型進行賦值操作,兩個變量互不影響。

let a = {
	age: 18,
	sex: "man"
};
let b = a;
console.log(a); //{age: 18, sex: "man"}
console.log(b); //{age: 18, sex: "man"}
a.age = 19;
console.log(a); //{age: 19, sex: "man"}
console.log(b); //{age: 19, sex: "man"}

對引用類型進行賦址操作,兩個變量指向同一個對象,改變變量 a 之後會影響變量 b。
通常在開發中並不希望改變變量 a 之後會影響到變量 b,這時就需要用到淺拷貝和深拷貝。

二、淺拷貝(Shallow Copy)

1、什麼是淺拷貝
創建一個新對象,這個對象有着原始對象屬性值的一份精確拷貝。如果屬性是基本類型,拷貝的就是基本類型的值,如果屬性是引用類型,拷貝的就是內存地址 ,所以如果其中一個對象改變了這個地址,就會影響到另一個對象。簡單來說可以理解爲淺拷貝只解決了第一層的問題,拷貝第一層的基本類型值,以及第一層的引用類型地址。

2.下面說說淺拷貝的幾個方法:

一.Object.assign(用於將所有可枚舉屬性的值從一個或多個源對象複製到目標對象。它將返回目標對象)
Object.assign方法實行的是淺拷貝,而不是深拷貝。也就是說,如果源對象某個屬性的值是對象,那麼目標對象拷貝得到的是這個對象的引用

let a = {
	age: 18,
	sex: "man",
	sports:["football","running","swimming"]
};

let b = Object.assign({},a);
console.log(a);
//  {
// 	age: 18,
// 	sex: "man",
// 	sports:["football","running","swimming"]
// };
console.log(b); 
	//  {
// 	age: 18,
// 	sex: "man",
// 	sports:["football","running","swimming"]
// };
a.sports.push("basketball")
console.log(a); 
		//  {
// 	age: 18,
// 	sex: "man",
// 	sports:["football","running","swimming","basketball"]
// };
console.log(b); 
	//  {
// 	age: 18,
// 	sex: "man",
// 	sports:["football","running","swimming","basketball"]
// };

上面代碼改變對象 a 之後,對象 b 的基本屬性保持不變。但是當改變對象 a 中的對象 sports 時,對象 b 也發生了變化。

二.Array.prototype.slice()
返回一個新的數組對象,這一對象是原數組的淺拷貝。原始數組不會被改變。

let a = ["1", "2", {
	age: 18,
	sex: "man",
	sports: ["football", "running", "swimming"]
}];

let b = a.slice(2);
console.log(b);
// [{
// 	age: 18,
// 	sex: "man",
// 	sports:["football","running","swimming"]
// }];
b[0].age = "19"
console.log(a);
// ["1","20",{
// 	age: 19,
// 	sex: "man",
// 	sports:["football","running","swimming"]
// }];
console.log(b);
// [{
// 	age: 19,
// 	sex: "man",
// 	sports:["football","running","swimming"]
// }];

實際開發中我們可能經常會碰到,對一個數組進行拷貝,然後對拷貝的數組進行操作,其實是會影響到原數組的,這點我們需要記住,Array.prototype.slice()是淺拷貝!

三、深拷貝(Deep Copy)

1、什麼是深拷貝
深拷貝會拷貝所有的屬性,並拷貝屬性指向的動態分配的內存。當對象和它所引用的對象一起拷貝時即發生深拷貝。深拷貝相比於淺拷貝速度較慢並且花銷較大。拷貝前後兩個對象互不影響。

2.下面說說淺拷貝的幾個方法:

一.JSON.parse(JSON.stringify())

let a = {
	age: 18,
	sex: "man",
	sports: ["football", "running", "swimming"]
};

let b = JSON.parse(JSON.stringify(a));
console.log(a);
//  {
// 	age: 18,
// 	sex: "man",
// 	sports:["football","running","swimming"]
// };
console.log(b);
//  {
// 	age: 18,
// 	sex: "man",
// 	sports:["football","running","swimming"]
// };
a.sports.push("basketball")
console.log(a);
//  {
// 	age: 18,
// 	sex: "man",
// 	sports:["football","running","swimming","basketball"]
// };
console.log(b);
//  {
// 	age: 18,
// 	sex: "man",
// 	sports:["football","running","swimming"]
// };

完全改變變量 a 之後對 b 沒有任何影響,這就是深拷貝。

看下對數組深拷貝效果如何

let a = ["1", "2", {
	age: 18,
	sex: "man",
	sports: ["football", "running", "swimming"]
}];

let b = JSON.parse(JSON.stringify(a.slice(2)));   
console.log(b);
// [{
// 	age: 18,
// 	sex: "man",
// 	sports:["football","running","swimming"]
// }];
b[0].age = "19"
console.log(a);
// ["1","20",{
// 	age: 18,
// 	sex: "man",
// 	sports:["football","running","swimming"]
// }];
console.log(b);
// [{
// 	age: 19,
// 	sex: "man",
// 	sports:["football","running","swimming"]
// }];

對數組深拷貝之後,改變原數組不會影響到拷貝之後的數組。

但是該方法有以下幾個問題。

(1)、會忽略 undefined

(2)、會忽略 symbol

(3)、不能序列化函數

(4)、不能解決循環引用的對象

(5)、不能正確處理new Date()

(6)、不能處理正則

let a = [{
	age: 18,
	sex: undefined,
	name: Symbol('zhangsan'),
	sports: function(){}
}];
console.log(a);
// [{
// 	age: 18,
// 	sex: undefined,
// 	name: Symbol('zhangsan'),
// 	sports: function(){}
// }];
let b = JSON.parse(JSON.stringify(a));   
console.log(b);
// [{
// 	age: 18,
// }]

undefined、symbol 和函數這三種情況,會直接忽略。

let obj = {
	a: 1,
	b: 2,
	c: {
		d: 3,
		e: 4
	}
};

obj.b = obj.c;
obj.c.d = obj.b;
let b = JSON.parse(JSON.stringify(obj));
console.log(b);
// Uncaught TypeError: Converting circular structure to JSON
// --> starting at object with constructor 'Object'
// --- property 'd' closes the circle
// at JSON.stringify (<anonymous>)

循環引用情況下,會報錯。

let time = new Date();
console.log(time)
//Sat May 30 2020 16:35:38 GMT+0800 (中國標準時間)
let time1 = JSON.parse(JSON.stringify(time));
console.log(time1);
//2020-05-30T08:35:38.991Z

new Date 情況下,轉換結果不正確。

let a = {
	age: 18,
	sports: /"football"/,
};
console.log(a)
// {
// 	age: 18,
// 	sports: /"football"/,
// }
let b = JSON.parse(JSON.stringify(a));
console.log(b);
// {
// 	age: 18,
// 	sports: {},
// }

正則情況下,轉換結果直接是個對象。

除了上面介紹的深拷貝方法,常用的還有jQuery.extend()等,有興趣的朋友可以自己去了解下!

四、總結

賦值,如果是引用類型,和原數據是指向同一對象,改變會使原數據一同改變,如果是基本類型,改變不會使原數據一同改變!淺拷貝的話,如果第一層有基本類型,改變基本類型不會影響原數據的基本類型數據,如果還有引用類型,改變引用類型會影響原數據的引用類型數據(淺拷貝只拷貝一層),深拷貝的話,不管是基本類型還是引用類型,怎麼改都不會影響原數據!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章