JavaScript 拷贝循环引用对象须用 structuredClone、手写 WeakMap 递归函数或 lodash.cloneDeep;前两者不支持函数/undefined/Symbol/class 实例,DOM 对象和闭包变量也无法真正拷贝。

JavaScript 中拷贝包含循环引用的对象,不能用 JSON.parse(JSON.stringify(obj)) 或浅拷贝(如 Object.assign、扩展运算符),因为前者会直接报错或丢弃循环引用,后者根本不会处理嵌套引用。必须使用能追踪已访问对象的深拷贝方案。
用 structuredClone(现代浏览器首选)
structuredClone 是原生支持循环引用的深拷贝方法,从 Chrome 98、Firefox 94、Safari 15.4 起可用,无需手动维护引用映射。
- 自动识别并正确重建循环引用,返回一个完全独立的新对象
- 支持 Map、Set、Date、RegExp、ArrayBuffer 等内置类型,但不支持函数、undefined、Symbol 和 class 实例(会丢失原型和方法)
- 用法极简:
const copy = structuredClone(original)
手写带引用追踪的深拷贝函数
当需兼容旧环境,或要支持函数、自定义类等特殊值时,可实现一个带 WeakMap 缓存的递归拷贝函数:
- 用
WeakMap存储「原对象 → 拷贝后对象」的映射,避免内存泄漏且支持任意键类型 - 每次进入对象/数组前先查缓存:若已存在,直接返回对应拷贝,中断递归,从而破除循环
- 对
null、基本类型直接返回;对Array、Object递归处理属性;对Date、RegExp等构造新实例
示例关键逻辑:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
const cache = new WeakMap();function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
if (cache.has(obj)) return cache.get(obj);
const copy = Array.isArray(obj) ? [] : {};
cache.set(obj, copy);
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
copy[key] = deepClone(obj[key]);
}
}
return copy;
}
借助第三方库(如 lodash)
lodash.cloneDeep 内部已完整处理循环引用、各种内置类型及自定义构造器,开箱即用且稳定。
- 安装:
npm install lodash,然后import { cloneDeep } from 'lodash' - 它通过内部引用表(类似 WeakMap)检测重复对象,比手写更健壮,尤其适合复杂业务对象
- 注意:如果对象含大量不可枚举属性、访问器(getter/setter)或 Symbol 键,需确认版本是否支持(v4.17+ 支持 Symbol)
什么情况下仍会出问题?
即使用了上述方法,以下情况仍无法“真正”拷贝:
- 函数内部闭包变量:函数体可复制,但其捕获的外部作用域不会被深拷贝
-
DOM/BOM 对象:如
document.body不可序列化,structuredClone会抛错,手写函数需显式跳过或定制处理 -
带有不可遍历属性的对象:如某些库用
Object.defineProperty设置了enumerable: false的属性,需额外遍历Object.getOwnPropertyNames或getOwnPropertyDescriptors

















