JavaScript中如何深拷贝一个对象?

来源:IPIPP.com作者:头衔:全栈工程师
导读:本期聚焦于小伙伴创作的《JavaScript中如何深拷贝一个对象?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《JavaScript中如何深拷贝一个对象?》有用,将其分享出去将是对创作者最好的鼓励。

在JavaScript中,对象是引用类型,直接赋值只会复制引用地址,修改新对象会影响原对象,因此深拷贝是开发中经常需要用到的操作。深拷贝指的是完全复制一个对象,新对象与原对象互不影响,修改任意一方都不会改变另一方的数据。

JavaScript中如何深拷贝一个对象?

常见深拷贝方法及局限性

1. JSON序列化与反序列化

这是最简单的深拷贝方式,通过JSON.stringify()将对象转为JSON字符串,再用JSON.parse()将字符串转回对象,实现深拷贝。

// JSON序列化实现深拷贝
const originObj = {
  name: 'test',
  age: 20,
  info: {
    hobby: 'reading'
  }
};
const copyObj = JSON.parse(JSON.stringify(originObj));
// 修改拷贝后的对象,不会影响原对象
copyObj.info.hobby = 'coding';
console.log(originObj.info.hobby); // 输出 reading

这种方式有明显局限性:无法拷贝函数、undefined、Symbol类型的值,会丢失这些属性;无法处理循环引用的对象,会直接报错;正则、Date等特殊对象会被转为普通对象,丢失原有特性。

2. 递归手动实现深拷贝

通过递归遍历对象的所有属性,判断属性类型后分别处理,可以实现更通用的深拷贝,能处理更多特殊场景。

// 递归实现深拷贝函数
function deepClone(target, map = new WeakMap()) {
  // 处理基本类型和函数,直接返回
  if (typeof target !== 'object' || target === null) {
    return target;
  }
  // 处理循环引用,如果已经拷贝过该对象,直接返回缓存的结果
  if (map.has(target)) {
    return map.get(target);
  }
  // 处理数组和对象
  const result = Array.isArray(target) ? [] : {};
  // 将当前对象存入缓存
  map.set(target, result);
  // 遍历自身可枚举属性
  for (const key in target) {
    if (target.hasOwnProperty(key)) {
      result[key] = deepClone(target[key], map);
    }
  }
  return result;
}

// 测试递归深拷贝
const obj1 = {
  name: 'demo',
  fn: function() { console.log('hello'); },
  date: new Date(),
  arr: [1, 2, 3]
};
// 添加循环引用
obj1.self = obj1;
const obj2 = deepClone(obj1);
console.log(obj2.fn); // 输出 [Function: fn]
console.log(obj2.date instanceof Date); // 输出 true
console.log(obj2.self === obj2); // 输出 true,循环引用处理正常

这种递归方式可以处理函数、循环引用,但是对正则、Map、Set等特殊内置对象的处理还不完善,需要额外添加类型判断逻辑。

3. 考虑特殊对象类型的完善递归实现

如果需要处理更多特殊对象类型,可以在递归函数中添加对应的类型判断分支。

function completeDeepClone(target, map = new WeakMap()) {
  // 基本类型和函数直接返回
  if (typeof target !== 'object' || target === null) {
    return target;
  }
  // 循环引用处理
  if (map.has(target)) {
    return map.get(target);
  }
  let result;
  // 处理不同类型对象
  if (target instanceof Date) {
    result = new Date(target);
  } else if (target instanceof RegExp) {
    result = new RegExp(target.source, target.flags);
  } else if (target instanceof Map) {
    result = new Map();
    map.set(target, result);
    target.forEach((value, key) => {
      result.set(completeDeepClone(key, map), completeDeepClone(value, map));
    });
    return result;
  } else if (target instanceof Set) {
    result = new Set();
    map.set(target, result);
    target.forEach(value => {
      result.add(completeDeepClone(value, map));
    });
    return result;
  } else {
    // 数组或普通对象
    result = Array.isArray(target) ? [] : {};
  }
  map.set(target, result);
  // 遍历属性拷贝
  for (const key in target) {
    if (target.hasOwnProperty(key)) {
      result[key] = completeDeepClone(target[key], map);
    }
  }
  // 处理Symbol类型的键
  const symbolKeys = Object.getOwnPropertySymbols(target);
  for (const symKey of symbolKeys) {
    result[symKey] = completeDeepClone(target[symKey], map);
  }
  return result;
}

不同深拷贝方法的选择建议

如果拷贝的对象只包含基本类型、普通对象和数组,没有特殊类型和循环引用,优先使用JSON序列化方式,代码简洁效率高。

如果对象包含函数、循环引用,或者需要保留Date、正则等对象的类型,使用递归实现的深拷贝函数,根据实际需求选择基础递归或完善版递归。

如果项目中深拷贝场景较多,也可以直接使用成熟的第三方库如lodash的cloneDeep方法,其已经处理了各类边界场景,稳定性更高。

JavaScript深拷贝对象拷贝递归拷贝JSON序列化修改时间:2026-06-03 00:34:31

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。