深拷贝递归复制整个对象树,包括嵌套对象、Date、RegExp等,确保原对象与拷贝完全独立;JSON.stringify() + parse仅适用于纯数据且丢弃undefined/function/Symbol等;手写需处理类型识别、特殊对象构造及循环引用;structuredClone()支持多类型但不兼容function/undefined/Symbol且有浏览器限制。

深拷贝不是简单地复制变量值,而是递归地复制整个对象树——包括所有嵌套的对象、数组、Date、RegExp 等引用类型值。原始对象和拷贝对象完全独立,修改任一对象的任意层级属性,都不会影响另一个。
这和 Object.assign() 或展开运算符 {...obj} 的浅拷贝有本质区别:它们只复制第一层属性,内部嵌套的对象仍共享同一内存地址。
这是最常被误用的“深拷贝方案”,它只适用于纯数据对象(Plain Object)和数组,且有多个硬性限制:
undefined、function、Symbol 字段会被直接丢弃Date 对象变成字符串(如 "2025-05-12T08:30:00.000Z"),丢失原型和方法RegExp 变成空对象 {}
TypeError: Converting circular structure to JSON
所以除非你 100% 确认输入是 JSON-safe 数据(比如后端返回的纯配置对象),否则不要把它当通用深拷贝手段。
一个健壮的手写实现必须处理类型识别、特殊对象构造、循环引用检测三类问题:
Object.prototype.toString.call() 精确判断类型,而不是仅靠 typeof 或 instanceof
Date、RegExp、Array 等需分别用 new Date(obj)、new RegExp(obj)、Array.from(obj) 构造新实例WeakMap 缓存已遍历的对象引用,遇到重复出现的同一对象时直接返回缓存结果,避免无限递归Object.create(Object.getPrototypeOf(obj)) 保持原型链,而不是简单 {}
示例片段(简化版):
function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
const type = Object.prototype.toString.call(obj);
let cloned;
if (type === '[object Array]') {
cloned = [];
} else if (type === '[object Date]') {
cloned = new Date(obj);
} else if (type === '[object RegExp]') {
cloned = new RegExp(obj);
} else {
cloned = Object.create(Object.getPrototypeOf(obj));
}
seen.set(obj, cloned);
for (const key in obj) {
if (Object.hasOwn(obj, key)) {
cloned[key] = deepClone(obj[key], seen);
}
}
return cloned;
}
structuredClone() 是目前最接近“开箱即用”的标准方案,支持 Map、Set、Date、RegExp、ArrayBuffer 等,也自动处理循环引用。
但它不是万能的:
function、undefined、Symbol —— 遇到会直接报错 DataCloneError
--enable-structured-cloning 标志如果项目需要兼容老浏览器,或需定制行为(例如忽略某些 key、转换日期格式),还是得回退到手写或第三方库(如 lodash.cloneDeep)。
循环引用、原型链、不可枚举属性、访问器属性(getter/setter)——这些细节一旦漏掉一个,就不是真正的深拷贝。别让“看起来没报错”骗过自己。