js 对象深复制(深拷贝)

对象深复制

复制逻辑

什么是深复制和浅复制?JavaScript有6种基本数据类型(Number,Boolean,String、null、udefined和Symbol)和1种引用类型(Object)。

  • 基本数据类型自身变量所指的内存,存的就是自身的值(String例外)
  • 引用数据类型存的是个引用地址(指针),具体数据存在堆中。
  • String在JavaScript比较特殊,它虽然是基本数据类型,但它却是按照引用类型在使用。 对于基本值类型(String)除外,直接复制,要想实现深复制,只要遇到引用类型,必须用new的方式重新创建,并且被复制对象的内部属性,如果是引用,也要这么处理。对象的属性有很多层,这就是深复制实现复杂的原因。

代码实现

首先我们创建一个对象来进行测试
 	  var d = Symbol();
      var e = Symbol();
      var ss = { a: 1 };
      var obj = {
        a: 1,
        b: 2,
        c: [1, 2, 3],
        zz: new Set([1, 2, ss]),
        yy: new Map(),
        [d]: "aaa",
        z: document.createElement("div"),
        d: {
          e: new Date(),
          f: /a/g,
          g: function (s) {
            console.log(s);
          },
          h: {},
        },
      };
      Object.defineProperties(obj.d.h, {
        i: {
          value: 10,
        },
        j: {
          configurable: true,
          writable: true,
          value: [1, 2, 3, 4],
        },
        k: {
          writable: true,
          value: {
            l: {},
            m: "abcde",
            n: true,
            o: [1, 2, 3],
          },
        },
        [e]: {
          value: ["a", "b", "c", "e"],
        },
      });

      Object.defineProperties(obj.d.h.k.l, {
        p: {
          value: function () {
            console.log("p");
          },
        },
        q: {
          value: {
            r: { a: 1 },
            j: { b: 2 },
          },
        },
      });
      var a_1 = { a: 1 };
      var a_2 = { b: 2 };
      obj.yy.set("name", "xietian");
      obj.yy.set(a_1, a_2);

		//对象深复制
const cloneObject = (source, target) => {
  if (source === null || source === undefined) return source;  //判断非空非undefined
  // if (source === document) return;  //非document
  if (!Object.prototype.isPrototypeOf(target)) {   //target是否已经存在对象原型上
    if (HTMLElement.prototype.isPrototypeOf(source)) {  //HTML复制
      target = document.createElement(source.nodeName);
    } else if (source.constructor === RegExp) {  //RegExp复制
      target = new RegExp(source.source, source.flags);
    } else if (source.constructor === Date) {  //Date复制
      target = new Date(source);
    } else if (source.constructor === Function) {   //Function 复制
      var arr = source.toString().replace(/\n|\r/g, "").trim().match(/\((.*?)\)\s*\{(.*)\}/).slice(1);  //进行source处理
      target = new Function(arr[0].trim(), arr[1]);
    } else if (source.constructor === Set) {   //Set复制
      target = new Set(cloneObject(Array.from(source.values())))
    } else if (source.constructor === Map) { // Map复制
      target = new Map();
      // entries() 方法返回一个数组的迭代对象,该对象包含数组的键值对 (key/value)。
      // 迭代对象中数组的索引值作为 key,数组元素作为 value。
      for (var [key, value] of source.entries()) {  //解构赋值
        if (Object.prototype.isPrototypeOf(key)) {   //判断Map中的key和value类型进行后续复制处理 引用类型继续clone
          if (Object.prototype.isPrototypeOf(value)) {
            target.set(cloneObject(key), cloneObject(value));
          } else {
            target.set(cloneObject(key), value);
          }
        } else {
          if (Object.prototype.isPrototypeOf(value)) {
            target.set(key, cloneObject(value));
          } else {
            target.set(key, value);
          }
        }
      }
    } else {
      target = new source.constructor();     //判断完毕后 剩下的数组和对象等 直接new一个source类型
    }
  }
  //getOwnPropertyNames方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。getOwnPropertySymbols返回symbol的值
  var names = Object.getOwnPropertyNames(source).concat(Object.getOwnPropertySymbols(source));
  for (var i = 0; i < names.length; i++) {
    // console.log(names[i]);
    if (source.constructor === Function && names[i] === "prototype") continue;  //函数会比较特殊  在这进行判断函数
    var desc = Object.getOwnPropertyDescriptor(source, names[i]);
    if (Object.prototype.isPrototypeOf(desc.value)) { //函数会因为它的prototype constructor指向的是一个函数 然后函数继续指向函数 所以会死循环
      // 根据需要将源对象的描述内容设置给当前目标对象
      // 相同属性名的描述内容,及值赋为刚才创建相同类型的对象
      Object.defineProperty(target, names[i], {
        enumerable: desc.enumerable,
        configurable: desc.configurable,
        writable: desc.writable,
        value: cloneObject(desc.value)
      });
    } else {
      // 如果描述的对象的值不是引用类型,
      // 直接将描述对象设置给目标对象的这个属性
      Object.defineProperty(target, names[i], desc);
    }
  }
  return target;
}
     
// a_1.a = 100;
// obj.d.h.j[1] = 1000;
const _obj = cloneObject(obj);
console.log(_obj);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MaxLoongLvs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值