ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

es-toolkit 深拷贝完全指南:`cloneDeep`(Lodash 兼容版)使用与源码实现解析

es-toolkit 深拷贝完全指南:`cloneDeep`(Lodash 兼容版)使用与源码实现解析 es-toolkit 深拷贝完全指南cloneDeepLodash 兼容版使用与源码实现解析【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit本文以 es-toolkit 的日本语兼容参考文档 docs/ja/compat/reference/object/cloneDeep.md 为核心骨架系统讲解 Lodash 兼容版cloneDeep的用法、循环引用处理、参数与返回值约定并结合仓库源码src/compat/object/cloneDeep.ts、src/object/cloneDeep.ts剖析其底层实现原理。读完本文你将掌握如何在 es-toolkit 中正确选择并使用深拷贝 API并理解它与structuredClone、浅拷贝的本质差异。一、先看结论优先使用 es-toolkit 原生版cloneDeep原文档开头即放置了一段醒目的警告值得首先强调请使用 es-toolkit 的cloneDeep这个cloneDeep函数指 compat 兼容版由于需要处理特殊对象类型的复杂逻辑相对较慢。 请改用更快速、更现代的 es-toolkit 原生版 cloneDeep。这意味着 es-toolkit 提供了两套cloneDeep版本导入路径定位原生版现代、更快import { cloneDeep } from es-toolkit/object日常开发首选仅覆盖常用类型实现精简Lodash 兼容版import { cloneDeep } from es-toolkit/compat面向从 Lodash 迁移的存量代码行为与 Lodash 对齐额外处理arguments对象、包装类型等特殊对象两套 API 的参数与返回值签名完全一致cloneDeep(value)接收任意值T返回深拷贝后的T。区别只在于内部覆盖的类型分支数量与性能特征。本文以兼容版为讲解主体即关联文档主题同时用原生版源码作对照。二、基础用法从原始值到嵌套对象兼容版cloneDeep的调用方式极为简单const cloned cloneDeep(value);原始值Primitive的拷贝原始值本身不可变因此直接按原值返回import { cloneDeep } from es-toolkit/compat; // 原始值的拷贝 const num 42; const clonedNum cloneDeep(num); // Returns: 42 (相同值)这一点在源码中也有对应证据原生版实现 src/object/cloneDeepWith.ts 中isPrimitive(valueToClone)会直接返回原值。数组的深拷贝数组中的每一层嵌套都会被完整复制为新的实例修改副本不会影响原数组// 数组的深拷贝 const arr [1, [2, 3], { a: 4 }]; const clonedArr cloneDeep(arr); clonedArr[1][0] 99; console.log(arr[1][0]); // 2 (原值未被修改) console.log(clonedArr[1][0]); // 99对象的深拷贝嵌套对象同样被逐层递归复制clonedObj.b.d.e与obj.b.d.e指向完全不同的内存实例// 对象的深拷贝 const obj { a: 1, b: { c: 2, d: { e: 3, }, }, }; const clonedObj cloneDeep(obj); clonedObj.b.d.e 99; console.log(obj.b.d.e); // 3 (原值未被修改) console.log(clonedObj.b.d.e); // 99Date 对象的拷贝Date会被复制为一个新的 Date 实例而非引用同一实例// Date 对象的深拷贝 const date new Date(2023-01-01); const clonedDate cloneDeep(date); // Returns: new Date(2023-01-01) (新的 Date 实例)源码中对应分支为 src/object/cloneDeepWith.tsnew Date(valueToClone.getTime())。三、复杂嵌套结构Map、Set 与 Date 的组合当数据结构中混入Map、Set、Date等内置类型时cloneDeep依然保证所有嵌套对象都以全新实例复制// 复杂的嵌套结构 const complex { arr: [1, { nested: true }], map: new Map([[key, { value: 1 }]]), set: new Set([{ item: 1 }]), date: new Date(), }; const clonedComplex cloneDeep(complex); // 所有嵌套对象都被复制为完全新的实例对应实现可参见 src/object/cloneDeepWith.tsMap创建新Map对每个 value 递归调用深拷贝后result.set(key, ...)Set创建新Set对每个元素递归深拷贝后result.add(...)。测试 src/object/cloneDeep.spec.ts 还专门验证了一个细节当同一个对象被Map中多个键引用时克隆后的Map内共享的是同一个克隆实例先修改原对象克隆结果不受影响。四、循环引用安全且保持引用关系深拷贝最大的坑之一是循环引用circular reference。直接递归会导致无限递归栈溢出而 es-toolkit 的cloneDeep通过内部维护一个Mapstack记录“已访问对象 → 已克隆对象”的映射来正确应对import { cloneDeep } from es-toolkit/compat; const obj { a: 1 }; obj.self obj; // 循环引用 const cloned cloneDeep(obj); console.log(cloned ! obj); // true console.log(cloned.self cloned); // true (循环引用被保留)输出结果说明两点cloned ! obj克隆体是全新对象cloned.self cloned克隆体内的自引用仍然指向克隆体自身引用拓扑被完整保留。源码视角stack 机制如何工作在 src/object/cloneDeepWith.ts 中递归前先检查stack.has(valueToClone)命中则直接返回已克隆的实例否则在复制前stack.set(valueToClone, result)预先登记保证后续任何指向该对象的引用都能拿到同一个克隆体。这正是测试 src/compat/object/cloneDeep.spec.ts 所覆盖的场景——包括一个由LARGE_ARRAY_SIZE 1个节点串联而成的“大量循环引用”链验证深拷贝在大规模环形结构下仍不溢出、且引用关系完全等价。五、参数与返回值原文档给出的正式签名如下参数value(T): 需要深拷贝的值。返回值(T): 返回深拷贝后的值。其中T为 TypeScript 泛型因此调用时类型信息会被完整保留——例如cloneDeepMyConfig(config)的返回值类型依旧是MyConfig无需手动断言。六、源码级深挖兼容版为何“慢”且“全”原文档明确提示兼容版“相对较慢因为要处理特殊对象类型的复杂逻辑”。具体“特殊”在哪里答案是兼容版在底层又叠加了一层 Lodash 行为对齐逻辑。兼容版的调用链src/compat/object/cloneDeep.ts 的完整实现只有一行export function cloneDeepT(obj: T): T { return cloneDeepWith(obj); }即直接委托给兼容版 src/compat/object/cloneDeepWith.ts后者在原生版基础上额外处理arguments对象复制为普通对象并手动补回length属性与Symbol.iterator对应测试 src/compat/object/cloneDeep.spec.ts包装类型Boxed PrimitiveNumber、String、Boolean对象通过new obj.constructor(obj.valueOf())重建再复制附加属性对应“expando properties”测试见 src/compat/object/cloneDeep.spec.ts无constructor的普通对象当getTag(obj) objectTag且typeof obj.constructor ! function时走{}copyProperties的兜底路径。这些额外的getTag/Object.prototype.toString分支判断正是兼容版相对较慢的原因。原生版支持的全部类型清单而原生版 src/object/cloneDeepWith.ts 的cloneDeepWithImpl则按类型分派逐类处理类型复制方式源码位置原始值直接返回L93-L95数组新建等长数组 递归含 RegExp 匹配结果的index/input属性L101-L122Datenew Date(getTime())L124-L126RegExp重建并保留lastIndexL128-L134Map/Set新建容器 元素递归L136-L156Buffersubarray()L158-L160TypedArray按原型构造器重建L162-L171ArrayBuffer / SharedArrayBufferslice(0)L173-L178DataView复制 buffer 复制属性L180-L187File/Blob按类型与名称/内容重建L190-L209Error及其子类structuredClone 恢复 message/name/stack/cause/constructorL211-L224包装类型 Boolean/Number/String重建包装对象并复制附加属性L226-L245普通对象/类实例Object.create(原型) 复制自有属性L247-L255属性复制与 getter 语义所有对象的属性复制统一由copyPropertiessrc/object/cloneDeepWith.ts完成遍历Object.keys与 Symbol 键对每个属性递归深拷贝。RegExp匹配数组的index/input、正则的lastIndex、包装对象的自定义属性expando properties等细节均有对应测试佐证src/compat/object/cloneDeep.spec.ts。此外原文档在 docs/reference/object/cloneDeep.md 中还补充了一个重要语义只读 getter 属性的返回值会被作为普通属性存储进拷贝对象。测试 src/object/cloneDeep.spec.ts 验证了这一点——克隆后get只读属性变为普通可读属性值。这意味着深拷贝天然“物化”了 getter适合用于快照、序列化前处理等场景。七、测试验证覆盖度全景围绕cloneDeep仓库提供了两套完整测试兼容版测试 src/compat/object/cloneDeep.spec.ts覆盖循环引用含超大规模环形链、arguments对象、类实例Foo、布尔/数字/字符串包装对象、正则、Map/Set、ArrayBuffer、Buffer、RegExp 匹配数组的index/input、正则lastIndex、expando 属性等原生版测试 src/object/cloneDeep.spec.ts覆盖原始值、嵌套数组/对象、Date、正则、Set、Map含多键共享同一对象的引用保持、类实例、File/Blob、ArrayBuffer、各类 TypedArray、Error及其全部内置子类与自定义子类、DataView、Buffer、只读属性、包装类型等。如果你希望在自己的项目里验证行为可以直接运行仓库的测试yarn vitest run src/compat/object/cloneDeep.spec.ts src/object/cloneDeep.spec.ts仓库使用 yarn具体脚本可查看 package.json 与 vitest.config.mts。八、实践建议何时用哪个版本综合原文档警告与源码分析给出如下选型建议新项目、无 Lodash 历史包袱一律使用import { cloneDeep } from es-toolkit/object的原生版类型覆盖已足够对象、数组、Date、RegExp、Map、Set、TypedArray、Error、File/Blob 等且实现更精简、更快正在从 Lodash 迁移的存量项目使用import { cloneDeep } from es-toolkit/compat的兼容版它对arguments对象、包装类型等 Lodash 特有行为做了对齐可以做到“改一行导入即可平替”对性能敏感的热路径优先原生版如需与structuredClone对比注意structuredClone无法复制函数与类实例而 es-toolkit 的cloneDeep能保留原型并复制类实例见 src/object/cloneDeep.spec.ts 的CustomClass测试。参考资料关联文档日文版docs/ja/compat/reference/object/cloneDeep.md英文版同主题文档docs/compat/reference/object/cloneDeep.md原生版 API 文档docs/reference/object/cloneDeep.md兼容版源码src/compat/object/cloneDeep.ts 与 src/compat/object/cloneDeepWith.ts原生版源码src/object/cloneDeep.ts 与 src/object/cloneDeepWith.ts测试用例src/compat/object/cloneDeep.spec.ts 与 src/object/cloneDeep.spec.ts【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表