javascript如何实现深拷贝_有哪些方法可以复制对象和数组【教程】

25次阅读

javaScript深拷贝需按场景选择:jsON.parse(json.stringify())最快但限制多;structuredClone()是现代标准解法,支持循环引用和多种内置类型;手写递归易漏边界情况,推荐用lodash.cloneDeep等成熟库。

javascript如何实现深拷贝_有哪些方法可以复制对象和数组【教程】

javascript 中没有内置的“深拷贝”函数,Object.assign() 和展开运算符 ... 都只做浅拷贝,遇到嵌套对象或数组会共享引用。真正需要深拷贝时,得根据场景选方法——不是越复杂越好,而是看是否要处理循环引用、特殊类型(dateregexpmapSet)、性能要求和浏览器兼容性。

JSON.parse(JSON.stringify()) 最快但限制最多

这是最常被用到的“取巧”方式,适合纯数据对象(只含 NULL、基本类型、普通对象、数组):

const obj = { a: 1, b: { c: 2 } }; const copy = JSON.parse(JSON.stringify(obj));
  • 会丢失 undefinedfunctionsymbolNaNInfinity,这些在序列化时被忽略或转成 null
  • 无法处理循环引用,直接报错 TypeError: Converting circular structure to JSON
  • Date 变成字符串RegExp 变成空对象,Map/Set 变成空对象
  • 性能好,但仅限于“能用”的简单场景

structuredClone() 是现代标准解法

chrome 98+、firefox 94+、safari 15.4+ 已支持 structuredClone(),它能正确处理 DateRegExpMapSetArrayBufferTypedArray,也支持循环引用:

const obj = { a: new Date(), b: new Map([['key', 'value']]) }; const copy = structuredClone(obj); // ✅ 正确复制
  • 不支持 functionundefinedSymbol —— 这些本来就不能被结构化克隆,会抛出 DataCloneError
  • 不能在所有环境中使用(如 node.js 17+ 才有,且需启用 --experimental-structured-cloning;旧版 Node 或 IE 完全不可用)
  • JSON 方案更可靠,只要环境支持,就应优先考虑

手写递归深拷贝要小心边界情况

如果必须兼容老环境,或需要定制行为(比如跳过某些字段、转换特定类型),就得自己实现。关键点不在“递归”,而在类型判断和特殊值处理:

立即学习Java免费学习笔记(深入)”;

function deepClone(obj) {   if (obj === null || typeof obj !== 'object') return obj;   if (obj instanceof Date) return new Date(obj);   if (obj instanceof RegExp) return new RegExp(obj);   if (obj instanceof Array) return obj.map(deepClone);   if (obj.constructor === Object) {     const clone = {};     for (let key in obj) {       if (Object.prototype.hasOwnProperty.call(obj, key)) {         clone[key] = deepClone(obj[key]);       }     }     return clone;   }   return obj; // 其他构造器实例(如 Map、Set)按需补充 }
  • 必须区分 ArrayObject,否则 Array.isArray() 检查漏掉会导致数组被当成对象遍历
  • 没处理循环引用的话,遇到自引用对象直接溢出
  • MapSetTypedArray 等需单独分支,否则内容丢失
  • 不推荐从零手写用于生产,容易漏 case;可用 lodash.cloneDeep 这类成熟库替代

第三方库如 lodash.cloneDeep 的实际取舍

lodash.cloneDeep() 覆盖了绝大多数边缘 case:循环引用、各种内置类型、原型链(可选)、甚至自定义克隆逻辑(通过 customizer)。但它体积大(约 70KB 压缩后),如果项目已用 Lodash,顺手调用没问题;如果只是为深拷贝引入整个库,就有点重了。

  • 注意:Lodash 的 cloneDeep 默认不保留 constructor,即克隆后对象的 constructorObject,不是原类型(除非手动配置)
  • webpack/Rollup 支持 tree-shaking,但 cloneDeep 内部依赖多,实际剔除效果有限
  • 对于微前端或轻量工具库,更倾向用 structuredClone + 降级 fallback(比如检测失败后用 JSON 方案并 warn)

深拷贝真正的复杂点从来不在“怎么写递归”,而在于你是否清楚当前数据里有什么——是纯 JSON 数据?有没有函数或正则?会不会出现循环引用?要不要跨线程structuredCloneWorker 里也生效)?选方法前,先 inspect 你的数据。

text=ZqhQzanResources