这个是我们要拷贝的对象obj 包含了多种类型
function fn () {}
fn.prototype.abc = 'abc'
const obj = {
a: 1,
b: 'b',
c: {
d: true
},
e: function () {
console.log('this is e');
},
f: [
{aa: 1},{bb:2}
],
g: undefined,
h: null,
i: Symbol,
j: new fn(),
}
先来看一下实现浅拷贝有几种方法
- 第一种方法可以使用Object.assign()
const copyObj = Object.assign({}, obj)
console.log(copyObj === obj)
// false
obj.c.d = 1
console.log(copyObj.c.d)
// 1
- 第二种方法使用展开运算符
const copyObj = {...obj}
console.log(copyObj === obj)
// false
obj.c.d = 1
console.log(copyObj.c.d)
// 1
- 第三种方法使用循环负值
const copyObj = {}
for (const i in obj) {
if (obj.hasOwnProperty(i)) {
copyObj[i] = obj[i]
}
}
// false
obj.c.d = 1
console.log(copyObj.c.d)
// 1
接下来我们来看一下深拷贝
- 第一种办法是使用JSON.stringify转换成字符串 然后JSON.parse再转成对象
const copyObj3 = JSON.parse(JSON.stringify(obj))
/*
{ a: 1,
b: 'b',
c: { d: true },
f: [ { aa: 1 }, { bb: 2 } ],
h: null,
j: {}
}
*/
console.log(copyObj3.j.abc)
// undefined
缺点:
1 .symbol undefind function 无法被转换 因为这三种类型在进行JSON.stringify的时候会过滤掉 并且无法序列化不可枚举属性
2. 经过转换后的对象无法继承原型链上的属性
- 第二种方法使用递归调用赋值
function deepClone(obj) {
// 判断是否是null或者不是对象类型
if (typeof obj !== 'object' || obj === null) {
return obj
}
let newObj = {}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
// 判断是数组还是对象
if (obj[key] instanceof Array) {
// 如果是数组循环数组 递归调用 因为数组元素有可能是对象
newObj[key] = []
obj[key].forEach((e, k) => {
newObj[key][k] = deepClone(e)
})
} else {
// 如果是对象 递归调用
newObj[key] = deepClone(obj[key])
}
}
}
return newObj
}
const copyObj = deepClone(obj)
obj.c.d = false
console.log(copyObj.c.d)
// true
这样我们实现了深拷贝
但是这样有一个问题是遇到循环引用会死循环
比如下边这个例子
const aa = {}
aa.bb = obj
obj.aa = aa
此时obj对象中的aa属性地址指向obj本身、这样进行深拷贝就会进入死循环
解决这个问题的方案其实很简单 只需要把经过转换的属性存起来 每次递归的时候都去读取一下看看是否被转换 如果被转换则直接返回不需要在尽心转换
这里我们使用map去存以转换的属性
// 增加一个hash表存储了元对象和拷贝对象的关系
function deepClone(obj, hash = new Map()) {
if (typeof obj !== 'object' || obj === null) {
return obj
}
// 拷贝前先去hash表中读取一下是否已存在 如果存在则直接返回hash表中的数据 避免进入死循环
if (hash.get(obj)) return hash.get(obj)
let newObj = {}
// 每次递归都去存储对应关系
hash.set(obj, newObj)
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] instanceof Array) {
newObj[key] = []
obj[key].forEach((e, k) => {
// 递归调用是将hash传入
newObj[key][k] = deepClone(e, hash)
})
} else {
// 递归调用是将hash传入
newObj[key] = deepClone(obj[key], hash)
}
}
}
return newObj
}
这样运行一下不会报错了