一、赋值(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()等,有兴趣的朋友可以自己去了解下!
四、总结
赋值,如果是引用类型,和原数据是指向同一对象,改变会使原数据一同改变,如果是基本类型,改变不会使原数据一同改变!浅拷贝的话,如果第一层有基本类型,改变基本类型不会影响原数据的基本类型数据,如果还有引用类型,改变引用类型会影响原数据的引用类型数据(浅拷贝只拷贝一层),深拷贝的话,不管是基本类型还是引用类型,怎么改都不会影响原数据!