方法一
const deepClone = (a) => JSON.parse(JSON.stringify(a))
缺点:
- 不支持 undefined, 函数,Date, 正则 等数据
- 不支持 循环引用
方法二
要对一个数据进行深拷贝,就需要对它的类型进行判断,JS中一共有8种数据类型, 其中复杂类型为 object, 其余7种为简单类型(string, number, undefined, null, boolean, bigint, symbol), 在 object 中又可以分为 Oject, Array, Function, Date, RegExp 等类,由此我们可以写成一个初始版本的深拷贝
- 如果传入的参数是基础类型则直接返回
- 如果传入的参数是object则对类进行判断
- 如果是函数需要判断它是普通函数还是箭头函数
- 如果是日期,则返回
new Date(a - 0)
, 其中 a-0 得到的是时间戳 - 如果是正则,则返回
new RegExp(a.source, a.flags)
- 如果是数组,则将结果初始化为
[]
- 如果是对象,则将结果初始化为
{}
接着遍历参数 a 的 key, 且用 hasOwnPropery 方法过滤 a 本身的 key, 对每个 key 递归调用深拷贝
function deepClone(a) {
if (a instanceof Object) {
let result
if (a instanceof Array) {
result = []
} else if (a instanceof Function) {
if (a.prototype) {
result = function(){return a.apply(this, arguments)}
} else {
result = (...args) => a.call(undefined, ...args)
}
} else if (a instanceof Date) {
result = new Date(a - 0)
} else if (a instanceof RegExp) {
result = new RegExp(a.source, a.flags)
} else {
result = {}
}
for (let key in a) {
if (a.hasOwnProperty(key)) {
result[key] = deepClone(a[key])
}
}
return result
} else {
return a
}
}
使用下面的代码测试,涉及到的类型都能进行深拷贝 ```javascript const a = { number: 1, bool: false, str: ‘hi’, empty1: undefined, empty2: null, array: [ {name: ‘jack’, age: 23}, {name: ‘rose’, age: 24} ], date: new Date(2000, 0, 1, 20, 30, 0), regexp: /.(j|t)sx/i, obj: {name: ‘jack’, age: 18}, f1: (a, b) => a + b, f2: function(a, b) {return a + b} }
const b = deepClone(a)
但是我们还没有考虑循环引用的情况,即当再添加一行下列代码,深拷贝就会报错了,会导致无限递归
```javascript
a.self = a
为了解决循环引用的问题,我们可以使用 Map 将进行过深拷贝的object类型存起来,在每次深拷贝object类型前都判断在 Map 中是否存在,若存在则直接返回 Map 中的值, 修改后的 deepClone 如下
function deepClone(a, map=new Map()) {
if (a instanceof Object) {
if (map.get(a)) {
return map.get(a)
}
let result
if (a instanceof Array) {
result = []
} else if (a instanceof Function) {
if (a.prototype) {
result = function(){return a.apply(this, arguments)}
} else {
result = (...args) => a.call(undefined, ...args)
}
} else if (a instanceof Date) {
result = new Date(a - 0)
} else if (a instanceof RegExp) {
result = new RegExp(a.source, a.flags)
} else {
result = {}
}
map.set(a, result)
for (let key in a) {
if (a.hasOwnProperty(key)) {
result[key] = deepClone(a[key], map)
}
}
return result
} else {
return a
}
}
在这里更加推荐使用 WeakMap,WeakMap只接受对象作为键名,且WeakMap的键名是对象的弱引用, 其所对应的对象可以被自动垃圾回收,当被垃圾回收时,WeakMap会自动移除该键值对。