怎么用递归算法手动实现一个支持循环引用的深拷贝

手动实现支持循环引用的深拷贝,关键在于记录已拷贝过的对象(或数组)及其对应的新副本,避免无限递归。递归本身不是问题,问题在于遇到重复引用时要“查表返回”,而不是继续深入。

核心思路:用 Map 记录“旧对象 → 新对象”的映射

每次进入递归前,先检查当前值是否已在 Map 中存在。若存在,直接返回对应的新对象;若不存在,新建副本、存入 Map,再递归处理其属性。

Map 的 key 是原始对象(包括普通对象、数组、Map、Set 等引用类型),value 是刚创建的对应副本 基础类型(string/number/boolean/null/undefined/Symbol/BigInt)直接返回,不进 Map 函数、Date、RegExp、ArrayBuffer 等特殊对象需单独判断并构造新实例

处理常见类型的具体逻辑

递归函数需区分类型做不同处理:

null 和 undefined:直接返回 基本类型(typeof x === ‘string’ 等):直接返回 对象或数组:先查 cache,命中则返回;否则 new Object() 或 [],存入 cache,再遍历键/索引递归赋值 Date / RegExp:new Date(obj) 或 new RegExp(obj) Map / Set:新建实例,递归拷贝 key 和 value(Map 的 key 也可能是对象,需同样走 cache) ArrayBuffer / TypedArray / DataView:需用 slice 或 buffer 复制底层内存 函数:通常浅拷贝(直接返回原函数),也可用 toString + eval(不推荐)或通过闭包模拟,但一般深拷贝不处理函数内部状态

一个精简可运行的手动递归实现(含循环检测)

以下为简化版,覆盖对象、数组、Date、Map、Set,支持循环引用:

function deepClone(obj, cache = new WeakMap()) { if (obj === null || typeof obj !== ‘object’) return obj; if (cache.has(obj)) return cache.get(obj); let cloned; if (obj instanceof Date) { cloned = new Date(obj.getTime()); } else if (obj instanceof RegExp) { cloned = new RegExp(obj); } else if (obj instanceof Map) { cloned = new Map(); cache.set(obj, cloned); obj.forEach((v, k) => cloned.set(deepClone(k, cache), deepClone(v, cache))); } else if (obj instanceof Set) { cloned = new Set(); cache.set(obj, cloned); obj.forEach(v => cloned.add(deepClone(v, cache))); } else if (Array.isArray(obj)) { cloned = []; cache.set(obj, cloned); obj.forEach((item, i) => cloned[i] = deepClone(item, cache)); } else { cloned = {}; cache.set(obj, cloned); for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { cloned[key] = deepClone(obj[key], cache); } } } return cloned;}

注意:使用 WeakMap 更安全(避免内存泄漏),key 必须是对象,且不会阻止垃圾回收;若需支持非对象 key(如字符串作为 Map 的 key),应改用普通 Map 并手动管理 key 的唯一性(但实际中极少需要)。

验证循环引用是否生效

测试代码示例:

const obj = { a: 1 };obj.self = obj; // 自引用const copy = deepClone(obj);console.log(copy.self === copy); // true ✅console.log(copy === obj); // false ✅

如果没加 cache,这段代码会因无限递归导致栈溢出;加上 WeakMap 后,第二次遇到 obj 时直接返回已创建的 copy,成功终止递归。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。