深拷贝是递归克隆对象属性,创建全新副本而非共享内存;需识别类型、递归处理并用weakmap防循环引用,避免json方法的类型丢失缺陷。

对象属性的递归克隆,本质就是深拷贝——即对嵌套的对象、数组、日期、正则等引用类型,也创建全新副本,而非共享内存地址。核心在于识别类型、递归处理、避免循环引用。
基础递归逻辑:判断 + 分支 + 递归
深拷贝不能只用 JSON.parse(JSON.stringify(obj)),它会丢失函数、undefined、Symbol、Date、RegExp、Map、Set 等,并且无法处理循环引用。手动实现需逐层判断数据类型:
- 如果是 null 或基本类型(string/number/boolean/bigint/symbol),直接返回原值
- 如果是 Date 或 RegExp,用构造函数新建实例(
new Date(obj)/new RegExp(obj)) - 如果是 Array 或普通 Object,先创建空容器(
[]或{}),再遍历其键/索引,对每个值递归调用深拷贝函数 - 遇到 Map/Set,需用对应构造函数并递归处理键值对或元素
关键细节:处理循环引用
当对象内部存在自引用(如 obj.a = obj)或相互引用时,不加防护会导致无限递归栈溢出。解决方法是维护一个“已拷贝映射表”(WeakMap):
- 每次进入拷贝前,先查 WeakMap 中是否已有该源对象的副本,有则直接返回副本
- 没有则新建目标对象,立即存入 WeakMap(源 → 副本),再继续递归填充属性
- WeakMap 能自动垃圾回收,且只接受对象作 key,适合做引用追踪
支持更多内置类型(可选增强)
标准深拷贝常需兼容常见内置对象:
-
Date:用
new Date(obj.getTime())或new Date(obj.toString()) -
RegExp:用
new RegExp(obj.source, obj.flags) -
Map:遍历
obj.entries(),递归拷贝每组[key, value] -
Set:遍历
obj.values(),递归拷贝每个元素后加入新 Set -
ArrayBuffer / TypedArray:用
obj.slice(0)或new obj.constructor(obj)
简易可运行示例(含循环引用防护)
以下是一个精简但健壮的实现片段:
function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
let cloned;
if (obj instanceof Date) cloned = new Date(obj);
else if (obj instanceof RegExp) cloned = new RegExp(obj.source, obj.flags);
else if (obj instanceof Array) cloned = [];
else if (obj instanceof Object) cloned = {};
else if (obj instanceof Map) cloned = new Map();
else if (obj instanceof Set) cloned = new Set();
seen.set(obj, cloned);
if (Array.isArray(obj)) {
for (let i = 0; i
<p>不复杂但容易忽略循环引用和特殊类型,写的时候多测几种边界情况,比如空对象、包含 NaN 的对象、带 getter 的对象(getter 不会被执行,除非显式处理)。</p>











