手动构建支持Date和RegExp的递归深拷贝函数需用Object.prototype.toString.call()精确识别类型,Date用new Date(obj.getTime())、RegExp用new RegExp(obj.source, obj.flags)创建副本,并用WeakMap缓存已克隆对象防止循环引用。

要手动构建一个支持 Date 和 RegExp 的递归深拷贝函数,核心在于准确识别类型、分别构造新实例,并防止循环引用导致的栈溢出。不能只靠 typeof 或 instanceof,必须用 Object.prototype.toString.call() 做精确判断。
正确识别 Date 和 RegExp 类型
JavaScript 中 typeof new Date() 和 typeof /abc/ 都返回 "object",无法区分。需统一用 toString.call() 提取内部标签:
Object.prototype.toString.call(new Date()) === "[object Date]"Object.prototype.toString.call(/abc/gi) === "[object RegExp]"
为 Date 和 RegExp 创建独立副本
直接 new Date(obj) 和 new RegExp(obj) 可行,但 RegExp 需显式提取 flags,否则会丢失修饰符(如 g、i、m):
- Date:用
new Date(obj.getTime())或new Date(obj.valueOf())都可 - RegExp:先读取
obj.source和obj.flags(ES6+ 支持),或兼容旧版用obj.global、obj.ignoreCase等逐个拼接
加入 WeakMap 防循环引用
若对象存在自引用或深层循环结构(如 a.b = a),不加缓存会导致无限递归甚至栈溢出。WeakMap 用来记录“原对象 → 已克隆对象”的映射:
- 每次进入递归前,先查 WeakMap 是否已有该对象的克隆结果
- 若有,直接返回缓存值;若无,创建新对象并存入 WeakMap 后再递归处理属性
完整可运行示例
以下是一个最小可用、覆盖基本类型、Date、RegExp、数组、普通对象且带循环检测的实现:
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj);
const tag = Object.prototype.toString.call(obj);
let cloned;
if (tag === '[object Date]') {
cloned = new Date(obj.getTime());
} else if (tag === '[object RegExp]') {
cloned = new RegExp(obj.source, obj.flags);
} else if (Array.isArray(obj)) {
cloned = [];
} else {
cloned = Object.create(Object.getPrototypeOf(obj));
}
hash.set(obj, cloned);
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = deepClone(obj[key], hash);
}
}
return cloned;
}










