浅拷贝
创建一个新对象,它会对原对象的属性进行一次拷贝,如果属性的值属于基本数据类型,那就直接拷贝这个值,如果属性的值,是引用类型,那就拷贝引用类型的指针(内存地址),所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
实现浅拷贝的一些方式
1. 可以利用数组的一些方法比如:slice、concat 返回一个新数组的特性来实现拷贝
let arr = [1,2,3];
let newArr = arr.concat();
newArr[0] = 4;
console.log(arr) // [1,2,3]
console.log(newArr) // [4,2,3]
2.Object.assign()
** 注意 Object.assign()第一个参数必须是个空对象
let obj = {a:1,b:2}
let obj2 = Object.assign({}, obj)
obj2.a = 4;
console.log(obj, obj2) // {a:1,b:2} {a:4,b:2}
3. 解构赋值
let obj = {a:1,b:2}
let obj2 = {...obj}
obj2.a = 4;
console.log(obj, obj2) // {a:1,b:2} {a:4,b:2}
自己实现一个浅拷贝
function lowDeepCopy (obj) {
if(typeof(obj) === 'object'){
let newObj = obj instanceof Array ? [] : {};
for(let key in obj) {
newObj[key] = obj[key]
}
return newObj
} else {
return obj;
}
}
实现深拷贝的一些方式
1. JSON.parse(JSON.stringify())
这个方法是我们日常开发过程中比较常用的方法,但是它也有它的一些弊端,我们通过下面的例子来简述一下JSON.parse(JSON.stringify())的弊端
let obj = {
a: 2,
fun: function () {
console.log(22);
},
symbol: Symbol('aaa'),
d: undefined,
reg: new RegExp('\\w+'),
err: new Error('ss'),
date: new Date(1536627600000) // Tue Sep 11 2018 09:00:00 GMT+0800
(中国标准时间)
};
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)
// 输出结果为
{
a:2,
date:"2018-09-11T01:00:00.000Z"
err:{},
nan:null
reg:{}
}
从上面的例子我们发现
- 如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象
- 如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失
let obj = {
fun: function () {
console.log(22);
}
};
let newArr = JSON.stringify(obj, function (key, value) {
if (typeof value === 'function') {
return value.toString();
} else {
return value;
}
});
console.log(newArr); // {"fun":"function fun() {\n console.log(22);\n }"}
- 如果obj里有NaN,则序列化的结果会变成null
- 如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式
时间对象格式问题,可以使用JSON.parse的第二个参数传入每个键值对的key和value进行处理
let obj = {
date: new Date(1536627600000)
};
let newArr = JSON.stringify(obj);
console.log(newArr); // {"date":"2018-09-11T01:00:00.000Z"}
let resetObj = JSON.parse(newArr, function (key, value) {
if (key === 'date') {
return new Date(value);
} else {
return value;
}
});
console.log(resetObj); // {date: Tue Sep 11 2018 09:00:00 GMT+0800 (中国标准时间)}
下面我们尝试着自己实现一个深拷贝
function deepCopy (obj) {
if (typeof obj === 'object') {
let newObj = obj instanceof Array ? [] : {};
for (let key in obj) {
newObj[key] = typeof (obj[key]) === 'object' ? deepCopy(obj[key]) : obj[key];
}
return newObj;
} else {
return obj;
}
}
循环引用
let arr = {
a: {
name: 'sj',
age: 28
},
b: 333
};
arr.c = arr;
let newArr = deepCopy(arr);
console.log(newArr, arr);
运行如上代码,会有爆栈的情况

我们考虑把拷贝前的对象作为key,把拷贝后的对象作为value,以一种键值对的方式,进行存储。
最常用的存储方式就是WeakMap和Map 相比较而言,WeakMap更合适
WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的
Map 对象间是存在强引用关系
弱引用与强引用相对,是指不能确保其引用的对象不会被垃圾回收器回收的引用。 一个对象若只被弱引用所引用,当下一次垃圾回收机制执行时,这块内存就会被释放掉。
优化一下我们之前的深拷贝代码
function clone(obj, map = new WeakMap()) {
if (typeof obj === 'object') {
let newObj = obj instanceof Array ? ? [] : {};
if (map.get(obj)) {
return map.get(obj);
}
map.set(obj, newObj);
for (let key in obj) {
newObj[key] = typeof (obj[key]) === 'object' ? deepCopy(obj[key], map) : obj[key];
}
return newObj;
} else {
return obj;
}
}