当前位置: 首页 > news >正文

JavaScript 深拷贝:手写一个简单可靠的 `deepClone` 函数

1. 为什么需要这个 deepClone 函数?

在 JavaScript 中,直接赋值对象或数组时,只是复制了引用(浅拷贝),修改新对象会影响原对象。例如:

const original = { a: 1, b: { c: 2 } };
const shallowCopy = original; // 浅拷贝
shallowCopy.b.c = 3;

console.log(original.b.c); // 3(原对象被意外修改)

深拷贝(Deep Clone) 可以彻底复制对象及其嵌套结构,使新旧对象完全独立。


2. deepClone 函数解析

function deepClone(obj) {
  // 1. 处理基本类型(null, number, string, boolean, undefined, symbol)
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  // 2. 处理 Date 对象
  if (obj instanceof Date) {
    return new Date(obj);
  }

  // 3. 处理 RegExp 对象
  if (obj instanceof RegExp) {
    return new RegExp(obj);
  }

  // 4. 初始化克隆对象(数组或普通对象)
  const cloneObj = Array.isArray(obj) ? [] : {};

  // 5. 递归拷贝所有属性
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(obj[key]);
    }
  }

  return cloneObj;
}

关键点解析

  1. 基本类型直接返回
    nullnumberstringbooleanundefinedSymbol 等直接返回,因为它们是不可变的。

  2. 处理 DateRegExp
    这两种对象需要特殊处理,否则直接赋值会导致引用共享。

  3. 递归拷贝对象属性
    遍历对象的所有自有属性(hasOwnProperty 过滤原型链属性),递归调用 deepClone 确保嵌套结构也被复制。


3. 使用示例

const original = {
  name: "Alice",
  age: 25,
  birthDate: new Date("1998-05-20"),
  skills: ["JavaScript", "React"],
  address: {
    city: "Beijing",
    zip: "100000"
  },
  regex: /hello/gi
};

const cloned = deepClone(original);

// 修改克隆对象,不影响原对象
cloned.age = 26;
cloned.skills.push("Vue");
cloned.address.city = "Shanghai";

console.log(original.age); // 25(未受影响)
console.log(original.skills); // ["JavaScript", "React"](未受影响)
console.log(original.address.city); // "Beijing"(未受影响)

4. 适用场景

普通对象、数组、DateRegExp
嵌套结构(多层对象/数组)
不需要处理循环引用的场景

5. 局限性

不支持循环引用(如 a.self = a,会导致栈溢出)
不处理 MapSetWeakMapWeakSet
不拷贝原型链上的属性


6. 如何改进?

如果需要更强大的深拷贝:

  1. 处理循环引用:使用 WeakMap 缓存已拷贝对象(增强版实现)。
  2. 支持更多类型:如 MapSetBlob 等。
  3. 性能优化:改用迭代方式避免递归栈溢出。

7. 总结

这个 deepClone 函数:

  • 代码简洁(仅 15 行)
  • 覆盖大部分常见需求(对象、数组、DateRegExp
  • 无第三方依赖(纯原生 JS 实现)

推荐在简单场景下使用,复杂数据建议用 lodash.cloneDeep 或完整版深拷贝。


你平时用什么方式实现深拷贝?欢迎讨论! 🚀

相关文章:

  • 2025华为软件精英挑战赛2600w思路分享
  • 互联网 Java 面试八股文汇总(2025 最新整理,持续更新)
  • 装饰器模式详解
  • 智能体项目实现AI对话流式返回效果
  • WebSocket原理详解(二)
  • 数据分析与应用3------数据清洗
  • 通过 Linux 网络命名空间实现路由器的方案与案例
  • PaddleX上线小目标检测模型产线,支持遥感分析、智能监控、智慧交通等领域高效应用
  • 今日八股——C++
  • 天气预报数据分析管理网站基于Spring Boot SSM原创
  • 青少年编程与数学 02-014 高中数学知识点 05课题、概率与统计
  • Docker的备份与恢复
  • 【PostgreSQL】【第3章】PostgreSQL的对象操作
  • C# ini文件全自动界面配置:打开界面时读ini配置到界面各控件,界面上的控件根据ini文件内容自动生成,点保存时把界面各控件的值写到ini里。
  • 建筑物自动化监测解决方案
  • Docker Api开启TLS认证流程
  • HTTP代理:网页加速的隐形引擎
  • 表单的前端数据流向
  • MATLAB之数据分析图系列:从二维到三维(直接套用)
  • 测试团队UI自动化实施方案
  • 趣看 | 五一黄金周:你拍风景,拍风景的人在拍你
  • 上海营商环境的“分寸”感:底线之上不断拓宽自由,底线之下雷霆制止
  • 刘诚宇、杨皓宇进球背后,是申花本土球员带着外援踢的无奈
  • 巴基斯坦宣布关闭全国空域48小时
  • 《黎明的一切》:与正常世界脱轨后,我选择不再回去
  • 科普|肩周炎的自限性,意味着不治也能自己好?