赋值,浅拷贝,深拷贝的区别

一、赋值(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()等,有兴趣的朋友可以自己去了解下!

四、总结

赋值,如果是引用类型,和原数据是指向同一对象,改变会使原数据一同改变,如果是基本类型,改变不会使原数据一同改变!浅拷贝的话,如果第一层有基本类型,改变基本类型不会影响原数据的基本类型数据,如果还有引用类型,改变引用类型会影响原数据的引用类型数据(浅拷贝只拷贝一层),深拷贝的话,不管是基本类型还是引用类型,怎么改都不会影响原数据!

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