深拷贝实现方法精讲:从 API 到手写(12 分钟讲透)

27 阅读2分钟

上节课讲了浅拷贝,浅拷贝有个“坑”,如果对象里有子对象,子数组(引用类型),拷贝的是地址,改一个会影响另一个。

let obj1 = { a: 1, b: { c: 2 } };
let obj2 = { ...obj1 }; // 浅拷贝
obj1.b.c = 3;
console.log(obj2.b.c); // 3(原对象子属性被改,拷贝对象也变了)

需要修改这个问题的话就需要「深拷贝」来解决。深拷贝的核心是:遇到引用类型(对象 / 数组),不拷贝地址,而是新建一个完全独立的引用类型,把原数据的值完整复制进去—— 最终新、旧对象彻底分离,改任何一个都不影响另一个。

下面看实现深拷贝的几种方法

方法 1:JSON.parse (JSON.stringify (obj))(开发首选,简单粗暴)

let obj1 = {
  a: 0,
  b: { c: 0 }, // 子对象(引用类型)
  d: [1, 2]    // 子数组(引用类型)
};
// 深拷贝核心代码
let obj2 = JSON.parse(JSON.stringify(obj1));
//stringify转JSON  parse转JS
// 测试:修改原对象的表层和深层属性
obj1.a = 1;
obj1.b.c = 1;
obj1.d.push(3);

console.log(obj1); // {a: 1, b: {c: 1}, d: [1,2,3]}
console.log(obj2); // {a: 0, b: {c: 0}, d: [1,2]}(完全独立,不受影响)

方法 2:手写深拷贝

核心逻辑是「递归 + 浅拷贝」:遍历对象,遇到基本类型直接拷贝,遇到引用类型(对象 / 数组)就递归调用深拷贝,直到所有层级都拷贝完成。

function deepCopy(object) {
  // 1. 边界判断:非对象/数组(null、基本类型)直接返回原数据
  if (!object || typeof object !== "object") return object;

  // 2. 创建新容器:根据原类型新建数组或对象
  let newObject = Array.isArray(object) ? [] : {};

  // 3. 遍历原对象,只拷贝自身属性(排除原型链)
  for (let key in object) {
    if (object.hasOwnProperty(key)) {
      // 核心:遇到引用类型就递归深拷贝,基本类型直接赋值
      newObject[key] = typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
    }
  }

  return newObject;
}
let obj1 = { a: 1, b: { c: 2 }, d: [3,4] };
let obj2 = deepCopy(obj1);

obj1.b.c = 100;
obj1.d.push(5);

console.log(obj2.b.c); // 2(子对象独立)
console.log(obj2.d); // [3,4](子数组独立)—— 基础版生效!