1. 什么是深拷贝?什么是浅拷贝?
不管是深拷贝还是浅拷贝,都是针对引用类型的。通俗的理解就是,深拷贝得到的对象不会受被拷贝对象影响,浅拷贝得到的对象与被拷贝对象之间相互影响。
2. 深拷贝的实现
- 方式一:手动深拷贝
let school = { name: "milly" }
let my = { age: { count: 18 }, name: "xy" }
let newMy = { ...my, age: { ...my.age } }
let all = { ...school, ...newMy }
console.log(all)
my.age.count = 100
console.log(my)
console.log(all) // { name: 'xy', age: { count: 18 } }
注意: 展开运算符本身是浅拷贝。Object.assign()
等同于...
,也是浅拷贝。
- 方式二:先把对象转换成字符串, 然后把字符串转换成对象
let school = { name: "milly" };
let my = { age: { count: 18 }, name: "xy" };
let all = JSON.parse(JSON.stringify({ ...school, ...my }));
console.log(all)
my.age.count = 300
console.log(my) // { age: { count: 300 }, name: 'xy' }
console.log(all) // { name: 'xy', age: { count: 18 } }
这种深拷贝的方式存在一定的问题:不能拷贝函数、undefined、时间、正则,测试看一下:
- 方式三:封装一个深拷贝 — 递归拷贝
首先, 我们可以判断数据类型的方式有:typeof
、instanceof
、Object.prototype.toString.call()
、constructor
先来看下浅拷贝函数是如何封装的:
function deepClone(obj) {
if (obj == null) {return obj}
if (obj instanceof Date) {return new Date(obj)}
if (obj instanceof RegExp) {return new RegExp(obj)}
if (typeof obj !== 'object' ) {return obj}
let cloneObj = new obj.constructor()
for (let key in obj) {
if (obj.hasOwnProperty) {
cloneObj[key] = obj[key]
}
}
return cloneObj
}
如何修改成深拷贝呢? 其实, 深拷贝 = 浅拷贝+递归
function deepClone(obj) {
if (obj == null) {return obj}
if (obj instanceof Date) {return new Date(obj)}
if (obj instanceof RegExp) {return new RegExp(obj)}
if (typeof obj !== 'object' ) {return obj}
let cloneObj = new obj.constructor()
for (let key in obj) {
if (obj.hasOwnProperty) {
cloneObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]
}
}
return cloneObj
}
// 测试
let obj = {
school: { name: { first: "mengmeng", last: "Liu" }, age: 18 },
fn: function () {},
aa: undefined,
b: null,
arr: [1, 2, 3],
}
let obj2 = deepClone(obj)
console.log(obj2)
obj2.school.age = 100
console.log(obj)
上述深拷贝函数已经能够实现开始的功能了,但是如果是拷贝自身的话,即obj.xxx=obj
,就会陷入死循环, 如何解决这个问题呢? 使用 弱引用 Map weakMap Set weakSet。
3. 使用弱引用解决深拷贝中递归死循环的问题
function deepClone(obj, hash = new WeakMap()) {
if (obj == null) {return obj}
if (obj instanceof Date) {return new Date(obj)}
if (obj instanceof RegExp) {return new RegExp(obj)}
if (typeof obj !== 'object' ) {return obj}
// 如果拷贝的是一个已经存在的对象,那就直接从WeakMap中返回即可
if (hash.has(obj)) { return hash.get(obj) }
let cloneObj = new obj.constructor()
hash.set(obj, cloneObj)
for (let key in obj) {
if (obj.hasOwnProperty) {
cloneObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key], hash) : obj[key]
}
}
return cloneObj
}