答案:JavaScript中实现深拷贝需处理类型差异和循环引用,使用WeakMap避免无限递归。支持Date、RegExp、原型链及循环引用,如a.self=a可正确克隆,确保b.self===b,完整方案优于JSON方法。

在JavaScript中实现真正意义上的深拷贝,尤其是处理循环引用,不能依赖简单的JSON.parse(JSON.stringify())方法,因为它无法处理函数、undefined、Symbol、日期、正则等类型,更无法识别循环引用,会导致报错。
要正确实现支持循环引用的深拷贝,核心思路是:使用WeakMap记录已拷贝的对象,避免无限递归。下面是一个完整且实用的实现方案。
1. 检测数据类型并分类处理
深拷贝需要根据不同的数据类型采取不同的复制策略:
- 基础类型(string、number、boolean、null、undefined、symbol、bigint)直接返回
- 不可遍历对象如Date、RegExp、Error、Function 直接构造或原样返回
- 可遍历对象(普通对象、数组)递归复制属性
2. 使用WeakMap追踪已访问对象
当遇到对象时,先检查是否已在WeakMap中存在对应的拷贝,若存在则直接返回,防止循环引用导致栈溢出。
立即学习“Java免费学习笔记(深入)”;
WeakMap适合这里,因为它的键是弱引用,不会影响垃圾回收。
3. 完整实现代码
function deepClone(obj, hash = new WeakMap()) {if (obj === null || typeof obj !== 'object') return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 处理包装对象
if (obj instanceof Boolean) return new Boolean(obj);
if (obj instanceof Number) return new Number(obj);
if (obj instanceof String) return new String(obj);
// 如果已拷贝过,直接返回缓存结果
if (hash.has(obj)) return hash.get(obj);
// 初始化目标对象,保持原构造函数
const clone = Array.isArray(obj)
? []
: Object.create(Object.getPrototypeOf(obj));
// 记录当前对象已被拷贝
hash.set(obj, clone);
// 递归拷贝所有可枚举属性
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], hash);
}
}
return clone;
}
4. 验证循环引用处理能力
测试用例如下:
const a = { name: 'John' };a.self = a; // 循环引用
const b = deepClone(a);
console.log(b.self === b); // true,结构正确
这个版本能正确保留原型链、处理数组、普通对象,并安全应对循环引用。
基本上就这些。不复杂但容易忽略细节,比如原型继承和内置对象的特殊处理。实际项目中也可考虑使用Lodash的_.cloneDeep,其内部实现更完善,但理解原理有助于排查边界问题。










