JSON.parse(JSON.stringify()) 与 lodash 的 cloneDeep:深度拷贝的比较与基础知识

发布于:2025-04-18 ⋅ 阅读:(19) ⋅ 点赞:(0)

JSON.parse(JSON.stringify()) 与 lodash 的 cloneDeep:深度拷贝的比较与基础知识
在 JavaScript 开发中,**深拷贝(Deep Copy)**是一个常见需求,尤其是在处理复杂对象和嵌套数据结构时。​JSON.parse(JSON.stringify(obj)) ​lodash 的 ​cloneDeep 函数都是实现深拷贝的常用方法。本文将详细探讨这两种方法的区别、各自的优缺点以及相关的基础知识,帮助你根据具体需求选择合适的深拷贝方式。


深拷贝 vs 浅拷贝

在讨论具体的深拷贝方法之前,首先需要理解 ​深拷贝​浅拷贝 的区别。

  • 浅拷贝(Shallow Copy)​:
    • 只复制对象的第一层属性。
    • 如果属性是引用类型(如对象、数组),复制的是引用,两个对象共享同一个内存地址,修改其中一个会影响另一个。
  • 深拷贝(Deep Copy)​:
    • 递归复制对象的所有层级。
    • 复制后的对象与原对象独立,修改一个不会影响另一个。

JSON.parse(JSON.stringify(obj)) 详解

工作原理

JSON.parse(JSON.stringify(obj)) 通过以下步骤实现深拷贝:

  • ​JSON.stringify(obj)
    • 将 JavaScript 对象转换为 JSON 字符串。
    • 只能序列化可枚举的自有属性,不支持函数、undefined、Symbol、Infinity、NaN 等特殊值。
    • 会将对象的结构和数据转换成 JSON 格式的字符串。
  • ​JSON.parse(jsonString)
    • 将 JSON 字符串解析为一个新的 JavaScript 对象。
    • 从而获得原对象的深拷贝。

优点

  • 简单易用
    • 仅使用原生 JavaScript 方法,无需引入外部库。
    • 语法简洁,一行代码实现深拷贝。
  • 适用于简单对象
    • 对于结构简单的对象(如普通 JSON 数据),效果良好。

缺点

  • 数据类型限

    • 无法复制函数:函数在序列化过程中会被忽略。
    • 无法复制特殊值
      • undefinedSymbol 会被转换为 null。
      • InfinityNaN 会被转换为 null 或 0。
      • Date 对象被转换为字符串。
      • RegExpMapSet 等复杂对象无法正确复制。
    • 循环引用
      • 如果对象内部存在循环引用(如 obj.a = obj),会抛出错误。
  • 性能开销

    • 对于大型对象或深度嵌套的对象,序列化和解析过程会带来较大的性能开销。
  • 不可处理函数和原型链

    • 无法保留对象的方法(函数)和继承自原型链的属性。

示例

const original = {
  name: "Alice",
  age: 30,
  hobbies: ["reading", "gaming"],
  address: {
    city: "Wonderland",
    zip: 12345
  },
  sayHello: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

const copy = JSON.parse(JSON.stringify(original));

console.log(copy);
/*
输出:
{
  name: "Alice",
  age: 30,
  hobbies: ["reading", "gaming"],
  address: { city: "Wonderland", zip: 12345 }
}
*/

copy.sayHello(); // TypeError: copy.sayHello is not a function

注意到 sayHello 方法在拷贝中被丢失,且对于 Date、RegExp 等类型也无法正确处理。


lodash 的 cloneDeep 详解

工作原理

lodash​cloneDeep 函数采用递归的方式,逐一复制对象的所有层级和属性,包括复杂的数据类型和原型链上的属性。它还能处理循环引用,保持对象的完整性。

优点

  • 支持多种数据类型
    • 可以正确处理对象、数组、函数、Date、RegExp、Map、Set 等复杂数据类型。
    • 保留 undefined、Symbol 等特殊值(部分情况下可能会有限制)。
  • 处理循环引用
    • 能够正确处理含有循环引用的对象,不会因递归导致堆栈溢出。
  • 性能优化
    • 尽管是第三方库,但在处理大型和复杂对象时,性能通常优于 ​JSON.parse(JSON.stringify())
  • 广泛的社区支持与稳定性
    • ​lodash 经过多年的开发和维护,拥有庞大的社区支持和丰富的功能模块。

缺点

  • 需要引入外部库
    • 增加项目的依赖体积。
    • 需要额外的安装步骤(但现代前端项目通常都会使用 lodash 或其他工具库)。
  • 可能存在性能瓶颈
    • 对于极大型或极端复杂的数据结构,虽然性能优越,但在某些特定场景下,仍可能比原生方法慢。

示例

import _ from 'lodash';

const original = {
  name: "Bob",
  age: 25,
  hobbies: ["traveling", "sports"],
  address: {
    city: "Adventureland",
    zip: 54321
  },
  createdAt: new Date(),
  sayHello: function() {
    console.log(`Hello, my name is ${this.name}`);
  },
  regexPattern: /abc/g
};

const copy = _.cloneDeep(original);

console.log(copy);
/*
输出:
{
  name: "Bob",
  age: 25,
  hobbies: ["traveling", "sports"],
  address: { city: "Adventureland", zip: 54321 },
  createdAt: Date对象,
  sayHello: function,
  regexPattern: /abc/g
}
*/

copy.sayHello(); // 输出: Hello, my name is Bob
console.log(copy.createdAt instanceof Date); // true
console.log(copy.regexPattern instanceof RegExp); // true

可以看到,cloneDeep 能够正确复制函数、日期对象、正则表达式等复杂数据类型。


常见误区与注意事项

  • ​JSON 方法无法处理函数和特殊值
    • 确保在不需要函数和特殊值的情况下使用。
  • JSON 方法会忽略 undefined 和 Symbol
    • 如果对象属性包含 undefined 或 Symbol,会被忽略,导致信息丢失。
  • JSON 方法不适用于循环引用
    • 如果对象内部有循环引用,使用 ​JSON 方法 会导致堆栈溢出错误。
  • ​lodash cloneDeep 的依赖问题
    • 如果项目本身已经使用了 lodash,那么使用 cloneDeep 是合理的选择。否则,引入整个 lodash 可能会增加打包体积。
  • 性能考虑
    • 虽然 ​lodash cloneDeep 功能强大,但在性能敏感的场景下,确保进行基准测试,以确认其满足性能要求。
  • 不可变性与引用类型
    • 即使进行了深拷贝,如果拷贝的属性是引用类型,并且在后续操作中未正确处理,依然可能出现引用问题。

总结

  • JSON.parse(JSON.stringify()) 是一种简便的深拷贝方法,但仅限于简单、可序列化的数据结构。它在性能上具有优势,但缺乏对复杂类型和特殊场景的支持。
  • ​lodash 的 cloneDeep 提供了更全面、更强大的深拷贝功能,能够处理复杂的数据类型和循环引用,但需要额外的依赖和可能稍低的性能(视场景而定)。

选择建议

  • 简单对象:使用 ​JSON.parse(JSON.stringify()),代码更简洁,性能更优。
  • 复杂对象:使用 ​lodash 的 cloneDeep,能处理复杂类型,提供更可靠的拷贝结果。

更多推荐阅读内容
如何在 JavaScript 中优雅地移除对象字段?
轻松掌握 Object.fromEntries:JavaScript中的实用技巧
如何将数组转换为对象(键为数组元素,值为 true)
JavaScript 嵌套数组扁平化的 5 种核心方案与深度实践指南
玩转JavaScript排序黑魔法:Infinity的正确打开方式
解锁数组操作新维度:flatMap 深度解析与实战指南


网站公告

今日签到

点亮在社区的每一天
去签到