最简单的办法可以使用:
JSON.parse(JSON.stringify());
这有一定的缺陷:function、Date数据、正则无法拷贝,无法处理循环对象等;且如果是面试时碰见这种问题,这样回答肯定是不行的,换一种思路,我们自己实现一个简易版:
module.exports = function clone(target) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
for (const key in target) {
cloneTarget[key] = clone(target[key]);
}
return cloneTarget;
} else {
return target;
}
};
但实际上这个没有对象存在循环引用的情况,即对象的属性间接或直接的引用了自身的情况,如果拷贝的值有循环引用的,则会陷入死循环,导致栈内存溢出
继续优化的话:
module.exports = function clone(target, map = new Map()) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {};
if (map.get(target)) {
return target;
}
map.set(target, cloneTarget);
for (const key in target) {
cloneTarget[key] = clone(target[key], map);
}
return cloneTarget;
} else {
return target;
}
};
这里是额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝,这样就巧妙化解的循环引用的问题。
这个存储空间,需要可以存储 key-value
形式的数据,且 key
可以是一个引用类型,我们可以选择 Map
这种数据结构:
- 检查
map
中有无克隆过的对象 - 有 - 直接返回
- 没有 - 将当前对象作为
key
,克隆对象作为value
进行存储 - 继续克隆
到这里其实也只考虑了Array和Object,但更深层次的就会更复杂,这里也不做详述了
------------------------------------------------------分割线------------------------------------------------------
近日又学到了一种实现深拷贝的方式,思想:只要对一个对象无限递归进行浅拷贝,最终的结果就是一个深拷贝
上代码:
module.exports = function deepClone(obj) {
const cloneObj = new obj.constructor()
// 一些特殊类型的处理
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
for (let i in obj) {
// 为了遍历一个对象的所有属性时忽略掉继承属性,使用hasOwnProperty来过滤该对象上的继承属性
if (obj.hasOwnProperty(i)) {
cloneObj[i] = deepClone(obj[i])
}
}
return cloneObj
}