深拷贝遇到循环引用时应主动检测并抛出自定义错误,核心是用weakmap记录已访问对象,若重复出现则立即报错。

JavaScript 中深拷贝遇到循环引用时,默认行为通常是栈溢出(RangeError: Maximum call stack size exceeded),但这个错误不明确,难以定位是哪一层对象导致的。要**主动检测并抛出自定义的循环引用错误**,核心思路是在递归遍历过程中记录已访问的对象引用,一旦重复遇到就立即报错。
用 WeakMap 记录已遍历对象
WeakMap 是最合适的工具:它以对象为键,不会阻止垃圾回收,且能准确识别同一对象的多次出现(包括嵌套中的循环引用)。
- 每次进入一个新对象(非原始值),先检查它是否已在
WeakMap中 - 如果存在,说明发生了循环引用,直接
throw new Error - 如果不存在,将该对象作为 key、一个标记(如
true或递归深度)存入WeakMap,再继续遍历其属性
基础实现示例(支持对象、数组、Date、RegExp 等)
以下是一个轻量、可扩展的深拷贝函数,带循环引用检测:
function deepClone(obj, visited = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
// 检测循环引用
if (visited.has(obj)) {
throw new Error('Circular reference detected while deep cloning');
}
// 标记当前对象已访问
visited.set(obj, true);
let result;
if (Array.isArray(obj)) {
result = [];
for (let i = 0; i
更健壮的处理建议
-
提供上下文信息:可在错误中附加路径(如
a.b.self),需在递归时传入路径字符串(例如deepClone(obj, visited, path = 'root')) -
支持 Map / Set / TypedArray:按需扩展判断逻辑(
obj instanceof Map等),同样需先查visited再递归其元素 -
避免污染原 visited 实例:若函数被多次调用,建议默认创建新
WeakMap,除非显式传入(如上例中参数设为可选) -
不依赖 JSON.stringify:因为
JSON.stringify遇到循环引用会静默忽略(返回undefined),无法报错,也不支持函数、undefined、Symbol 等
为什么不用 try/catch 捕获 RangeError?
不能依赖捕获 RangeError 来检测循环引用:它发生在调用栈真正溢出时,此时可能已执行大量无效递归,性能差且错误位置模糊;而主动检测能在第一次重复引用时立刻中断,精准、高效、可控。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











