深拷贝在 JavaScript 中的几种实现方式对比

深拷贝在 JavaScript 中的几种实现方式对比

在开发过程中,我们经常需要对对象进行深拷贝,以便创建一个数据的完全独立副本,避免不小心修改原始数据。常见的深拷贝方法有以下几种:
• JSON 序列化法(JSON.stringify/JSON.parse)
• 结构化克隆(structuredClone)
• 自定义深拷贝函数

下面将详细介绍每种方法,并讨论它们在效率和适用场景上的不同。

1. JSON 序列化法

原理与使用

通过 JSON.stringify() 将对象转为 JSON 字符串,再利用 JSON.parse() 还原为新对象。例如:

js 复制代码
const deepCopyJson = (obj) => {
  return JSON.parse(JSON.stringify(obj));
};

优点:

• 实现简单,代码少。

• 对于简单对象(不包含函数、undefined、Symbol、循环引用等)非常有效。

缺点:

• 无法处理特殊数据类型:例如 Date、RegExp、Map、Set、undefined、Infinity 等会丢失或转换不正确。

• 当对象数据量较大时,序列化和反序列化过程可能比较耗时。

• 对于包含循环引用的对象会抛错。

适用场景:
加粗样式 • 对象结构简单,数据格式固定时(例如配置数据或简单的 JSON 数据)。

2. 结构化克隆(structuredClone)

原理与使用

现代浏览器提供了原生的 structuredClone() 方法,利用浏览器内部优化的结构化克隆算法,能够高效地复制多种复杂数据结构。使用示例:

js 复制代码
if (typeof structuredClone === 'function') {
  const newObj = structuredClone(obj);
}

优点:

• 原生支持,效率较高,尤其适用于大型或复杂对象。

• 能够处理更多类型的数据:如 Map、Set、ArrayBuffer、Date 等。

• 内部实现针对循环引用也能较好地处理。

缺点:

• 浏览器兼容性需要注意,老旧环境可能不支持。

• 对于小对象,可能没有明显优势,有时反而比 JSON 方法稍慢(不过整体差异不大)。

适用场景:

• 对象结构复杂或包含特殊数据类型时;

• 对性能有较高要求且运行环境支持该 API 时。

3. 自定义深拷贝函数

原理与使用

在无法使用 JSON 或 structuredClone 的情况下,可以手动编写递归函数来深度拷贝对象。下面是一个简单的实现:

js 复制代码
const deepCopy = (item) => {
  if (item === null || typeof item !== 'object') {
    return item;
  }
  
  if (Array.isArray(item)) {
    return item.map(deepCopy);
  }
  
  const result = {};
  for (const key in item) {
    if (Object.prototype.hasOwnProperty.call(item, key)) {
      result[key] = deepCopy(item[key]);
    }
  }
  return result;
};

const newObj = deepCopy(obj);

优点:

• 灵活性高,可以根据需要扩展以处理更多特殊情况。

• 不依赖环境提供的 API,兼容性好。

缺点:

• 需要手动处理各种边界情况,代码实现和维护较复杂。

• 性能上通常不及浏览器原生实现的 structuredClone。

适用场景:

• 对兼容性有要求的项目或需要定制拷贝逻辑的场景;

• 数据结构较简单,且不频繁调用时。

性能对比与选择建议

性能比较

  • 小型对象:
    JSON 方法通常性能较好,因为序列化过程开销较低,且实现简单。
  • 复杂对象:
    对于包含特殊数据类型或嵌套层次较深的对象,structuredClone 通常表现更优,因为它是浏览器内部实现的,并针对复杂场景做了优化。
  • 自定义函数:
    一般来说,自定义递归实现的性能不及结构化克隆,但可以针对具体需求进行优化。

综合建议:

  1. 优先选择 JSON 方法:当你确定对象数据格式简单,并且不会包含无法序列化的内容时。
  2. 考虑 structuredClone:如果运行环境支持且对象较大或结构复杂,使用 structuredClone() 更能保证数据完整性和性能。
  3. 自定义深拷贝:当需要对数据进行特殊处理或兼容不支持 structuredClone 的环境时,可以采用自定义函数,但要注意性能优化和代码健壮性。

示例代码整合

下面给出一个综合示例,根据对象大小自动选择拷贝方式,并在异常时捕获错误:

js 复制代码
function efficientDeepCopy(obj) {
  try {
    if (!obj) return {};

    // 对于小型对象,使用 JSON 方法可能更快
    if (JSON.stringify(obj).length < 10000) {
      return JSON.parse(JSON.stringify(obj));
    }
    // 使用结构化克隆(如果可用)
    if (typeof structuredClone === 'function') {
      return structuredClone(obj);
    }

    // 自定义深拷贝函数,处理常见数据类型
    const deepCopy = (item) => {
      if (item === null || typeof item !== 'object') {
        return item;
      }
      if (Array.isArray(item)) {
        return item.map(deepCopy);
      }
      const result = {};
      for (const key in item) {
        if (Object.prototype.hasOwnProperty.call(item, key)) {
          result[key] = deepCopy(item[key]);
        }
      }
      return result;
    };

    return deepCopy(obj);
  } catch (error) {
    console.error('efficientDeepCopy error:', error);
    return {};
  }
}

在这个示例中,根据对象序列化后的长度来判断是否使用 JSON 方法;如果对象较大且环境支持 structuredClone,则优先使用它;否则回退到自定义深拷贝函数。

总结

  • JSON 方法:适用于简单数据,性能好,但局限性明显。
  • structuredClone:适用于复杂和大型对象,兼容性需要关注,但总体性能较高。
  • 自定义深拷贝:灵活但复杂,一般作为最后的备用方案。

通过对比,可以根据项目的具体需求和对象的特点,选择最合适的深拷贝方式,从而在性能与功能之间取得平衡。

相关推荐
崔庆才丨静觅27 分钟前
hCaptcha 验证码图像识别 API 对接教程
前端
曹牧1 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
passerby60611 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了1 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅1 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅2 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
爬山算法2 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
kfyty7252 小时前
集成 spring-ai 2.x 实践中遇到的一些问题及解决方案
java·人工智能·spring-ai
猫头虎2 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
李少兄2 小时前
在 IntelliJ IDEA 中修改 Git 远程仓库地址
java·git·intellij-idea