JavaScript深拷贝复杂因对象仅存引用,嵌套加深易栈溢出,循环引用需WeakMap检测;混合类型(Date/RegExp/Map等)克隆方式各异;原型与不可枚举属性易丢失;性能与安全需依场景权衡。
JavaScript 中深拷贝对嵌套引用的处理之所以复杂,根本原因在于:对象和数组本身不存数据,只存指向内存地址的引用。一旦嵌套层级变深、类型变杂、结构出现循环,简单的复制逻辑就会失效或崩溃。
深拷贝必须逐层进入对象/数组内部,对每个值判断类型并决定如何复制。遇到多层嵌套(如 {a: {b: {c: {d: {...}}}}})时,递归深度可能超出调用栈限制;若结构中存在环状引用(A → B → A),不做检测就会无限递归,最终触发 “Maximum call stack size exceeded” 错误。
WeakMap 存储原始对象 → 拷贝后对象的映射关系一个真实业务对象常混杂普通属性、数组、Date、RegExp、Map、Set、null、undefined、甚至函数和 Symbol。而每种类型的构造与克隆方式完全不同:
Date 需用 new Date(original.getTime()) 或 original.toISOString() 再解析RegExp 要提取 source 和 flags,再用 new RegExp(source, flags)
Map/Set 不能靠 JSON.stringify,需遍历键值对重建function 和 Symbol 通常不拷贝(语义上不可复制),但需明确保留还是丢弃null 和 undefined 可直接返回,但要注意 typeof null === 'object' 的陷阱原生 JSON.parse(JSON.stringify()) 或简单递归只遍历可枚举自有属性,会丢失:
立即学习“Java免费学习笔记(深入)”;
Object.defineProperty 设置的 enumerable: false 属性若需完整复刻行为一致性(例如拷贝一个带自定义 toString 的类实例),就得手动获取所有自有属性(Object.getOwnPropertyDescriptors)并重建原型关系。
完全通用的深拷贝函数必然带来开销:遍历、类型判断、映射查找、构造新实例……尤其在大数据量或高频调用场景下,可能成为瓶颈。
JSON.parse(JSON.stringify(obj)),快且安全lodash.cloneDeep,它做了大量优化和类型特判