深拷贝的实现

175 阅读2分钟

利用递归

递归创建新的对象至关重要

let obj = {name: 'han',age: 20,hn: {jk: 123, cd:231}}
  function copy(obj){
    let tem = {}//当递归回来时,因为又是创建了一个对象,这样就可以深拷贝到obj里的对象,而不是赋值地址
    for (const key in obj) {
      tem[key] = typeof obj[key] === "object"?copy(obj[key]):obj[key];
    }
    return tem;
  }
  let tem2 = copy(obj);
  console.log(tem2);//hn: {jk: 123, cd: 231}
  obj.hn.jk = 360;
  console.log(tem2);//hn: {jk: 123, cd: 231}

此时我们可以看到,改变obj.hn的值tem2对象也不会改变,证明深拷贝成功

但是如果是数组类型呢?此时如果还用上述方法,因为数组是用typeof判断也是object类型,最后拷贝出来后数组也会变成了对象。

此时我们用instanceof来判断传过去的形参obj的原型链上是否有Array的构造函数

此时还用到了Object.entries()函数,可以直接遍历键值对方式

用法:

const object1 = {
  a: 'somestring',
  b: 42
};

for (let [key, value] of Object.entries(object1)) {//
  console.log(`${key}: ${value}`);
}





所以最终方案为:

let obj = {name: 'han',age: 20,arr: [],hn: {jk: 123, cd:231}}
  function copy(obj){
    let tem = obj instanceof Array?[]:{};//用instanceof来判断传过去的形参obj的原型链上是否有Array的构造函数
    
    
    for (const [k,v] in Object.entries(obj)) {//Object.entries()函数,可以直接遍历键值
      tem[k] = typeof v === "object"?copy(v):v;
    }
    return tem;
  }
  let tem2 = copy(obj);
  console.log(tem2);//age: 20 arr: [] hn: {jk: 123,cd: 231} name: "han"
  obj.hn.jk = 360;
  console.log(tem2);//age: 20 arr: [] hn: {jk: 123,cd: 231} name: "han

还可以利用JSON.parse(JSON.stringfy())进行深拷贝

根据不包含引用对象的普通数组深拷贝得到启发,不拷贝引用对象,拷贝一个字符串会新辟一个新的存储地址,这样就切断了引用对象的指针联系因为JOSN对象中的stringify可以把一个js对象序列化为一个JSON字符串,所以我们在进行对对象字符串序列化的时候相当于创建了一个全新的对象,再利用JSON.parse反序列化即可得到一个对象的复制

存在的缺陷

1.如果json里面有时间对象,则序列化结果:时间对象=>字符串的形式;

{
    let obj = {
        age: 18,
        date: new Date()
    };
    let objCopy = JSON.parse(JSON.stringify(obj));
    console.log('obj', obj);
    console.log('objCopy', objCopy);
    console.log(typeof obj.date); // object
    console.log(typeof objCopy.date); // string
}

  1. 如果json里有 function,undefined,则序列化的结果会把 function,undefined 丢失;
{
    let obj = {
        age: 18,
        fn: function () {
            console.log('fn');
        },
        hh: undefined
    };
    let objCopy = JSON.parse(JSON.stringify(obj));
    console.log('obj', obj);
    console.log('objCopy', objCopy);
}

  1. 如果json里有NaN、Infinity和-Infinity,则序列化的结果会变成null;
{
    let obj = {
        age: 18,
        hh: NaN,
        isInfinite: 1.7976931348623157E+10308,
        minusInfinity: -1.7976931348623157E+10308
    };
    let objCopy = JSON.parse(JSON.stringify(obj));
    console.log('obj', obj);
    console.log('objCopy', objCopy);
}
  1. 如果json里有对象是由构造函数生成的,则序列化的结果会丢弃对象的 constructor
{
    function Person(name) {
        this.name = name;
    }
    let obj = {
        age: 18,
        p1: new Person('lxcan')
    };
    let objCopy = JSON.parse(JSON.stringify(obj));
    console.log('obj', obj);
    console.log('objCopy', objCopy);
    console.log(obj.p1.__proto__.constructor === Person); // true
    console.log(objCopy.p1.__proto__.constructor === Object); // true
}