写在前面
在讨论深拷贝之前要先搞清楚一些概念:
什么是拷贝?
拷贝就是复制
拷贝的分类
拷贝,即复制分为两种,一个是深拷贝,一个是浅拷贝,深拷贝是对应于 JS 中的值数据类型的复制,直接就是复制的实际的值。而当遇到引用数据类型,即对象的复制时,如果仅仅用浅拷贝则拷贝的是引用的地址,而不是引用对象本身。
深拷贝的方法?
如下所示,这里主要介绍两种
1. JSON.parse(JSON.stringify(obj))
利用 JSON 对象先将对象序列化(即字符串化),再将其反序列化(即转化为对应的JS数据类型),即可得到一个深拷贝的对象。
let obj1 = {
a: 1,
b: 2
}
let obj3 = JSON.parse(JSON.stringify(obj1))
obj3.b = 5
console.log(obj1) //{ a: 1, b: 2 }
console.log(obj1 === obj3) //false
但是JSON的方式有局限性,就是对象必须遵从JSON的格式,当遇到层级较深,且序列化对象不完全符合JSON格式时,使用JSON的方式进行深拷贝就会出现问题。
let obj1 = {
a: '1',
b: '2',
c: function func() {}
}
let obj4 = JSON.parse(JSON.stringify(obj1))
console.log(obj4) //{ a: '1', b: '2' }出错
2. 递归深拷贝
用递归的方法实现深拷贝
深拷贝需要考虑的事情:
- 递归
- 判断类型
- 不拷贝原型上的属性
- 检查环
function deepClone(source, hash = new Map()) {
if(hash.has(source))) {
return hash.get(source);
}
if(source instanceof Object) {
let result = {};
if(source instanceof Function) {
// 箭头函数不能用作构造函数,因此没有 prototype 原型
if(source.prototype) {
result = function(...args) {
source.call(this, ...args);
}
} else {
result = (...args) => {
source(...args);
}
}
} else if(source instanceof Array) {
result = [];
} else if(source instanceof Date) {
result = new Date(source);
} else if(source instanceof RegExp) {
result = new RegExp(source.source, source.flags);
}
hash.set(source, result);
for(let key in source) {
if(source.hasOwnProperty(key)) {
result[key] = deepClone(source[key])
}
}
return result;
} else {
return source;
}
}