手写深拷贝
方法一,用JSON
javascript
const b = JSON.parse(JSON.stringify(a))
这个方法有如下缺点:
- 不支持
Date
、正则 、undefined
、函数等数据。 - 不支持引用(即环状结构)。
方法二,用递归
要点
- 递归
- 判断类型(首先判断基本类型、引用类型,然后引用类型在细分)
- 检查环(使用Map/weakMap检查,因为因为只有Map/weakMap的key可以使用对象,Object的key只能使用string/symbol)
- 不拷贝原型上的属性
javascript
/**
* @desc 深拷贝
* @param obj
* @param cache {Object} 缓存不能全局,临时创建并递归传递
*/
function deepClone(obj, cache = new Map()) {
// 区分普通类型和复杂引用类型
if (obj instanceof Object) {
if (cache.has(obj)) return cache.get(obj)
let result
if (obj instanceof Function) {
if (obj.prototype) {
result = function (...args) {
return obj.apply(this, args)
}
} else {
result = (...args) => {
return obj.call(undefined, ...args)
}
}
} else if (obj instanceof Date) {
result = new Date(obj - 0)
} else if (obj instanceof RegExp) {
result = new RegExp(obj.source, obj.flags)
} else if (obj instanceof Array) {
result = []
} else {
result = {}
}
cache.set(obj, result)
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key], cache)
}
}
return result
} else {
return obj
}
}
javascript
function deepClone(obj, cache = new Map()) {
// 处理基础类型(null、undefined、原始类型)
if (obj == null || typeof obj !== 'object') return obj;
// 检查环(循环引用)
if (cache.has(obj)) return cache.get(obj);
// 判断具体类型(支持更多类型如 Date、RegExp、Map、Set 等)
const type = Object.prototype.toString.call(obj);
let clone;
switch (type) {
case '[object Date]':
clone = new Date(obj.getTime());
break;
case '[object RegExp]':
clone = new RegExp(obj.source, obj.flags);
break;
case '[object Map]':
clone = new Map();
cache.set(obj, clone); // 提前缓存,避免循环引用
for (const [key, value] of obj.entries()) {
clone.set(deepClone(key, cache), deepClone(value, cache));
}
return clone;
case '[object Set]':
clone = new Set();
cache.set(obj, clone);
for (const value of obj.values()) {
clone.add(deepClone(value, cache));
}
return clone;
case '[object Function]':
if (obj.prototype) {
clone = function (...args) { return obj.apply(this, args); }
} else {
clone = (...args) => {
return obj.call(undefined, ...args)
}
}
default:
clone = Array.isArray(obj) ? [] : {};
}
// 缓存当前对象,避免后续循环引用
cache.set(obj, clone);
// 遍历自身属性(不拷贝原型链上的属性)
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], cache); // 递归拷贝
}
}
return clone;
}