JS对象深拷贝

217 阅读2分钟

深拷贝

浅拷贝只是创建了一个新的对象,复制了原有对象的基本类型的值,而引用数据类型只拷贝了一层属性,再深层的还是无法进行拷贝。深拷贝则不同,对于复杂引用数据类型,其在堆内存中完全开辟了一块内存地址,并将原有的对象完全复制过来存放。

深拷贝后的对象与原始对象是相互独立、不受影响的,彻底实现了内存上的分离

var obj = {
     a: function () { console.log('hello world最强写手') },
     b: { c: 5 },
     c: [0, 1, 2],
     d: 'jack',
     e: new Date(),
     f: null,
     g: undefined
}
var clone = function (obj) {
    return JSON.parse(JSON.stringify(obj));
}
var b = clone(obj);
console.log(b); 

上述clone的方法会忽略function和undefined的字段. 而且只能克隆原始对象自身的值,不能克隆它继承的值.

JSON.parse(JSON.stringify())使用时要注意的点:
  1. 如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象;

    var test = {
         name: 'a',
         date: [new Date(1656390281537), new Date(1656390297387)],
       };
    let b;
    let c;
    c = JSON.parse(JSON.stringify(test));
    console.log(c);
    console.log(typeof c.date[0]); //string
  2. 如果obj里有RegExpError对象,则序列化的结果将只得到空对象;

    const test = {
          name: 'a',
          date: new RegExp('\w+'),
    };
    const copyed = JSON.parse(JSON.stringify(test));
    test.name = 'test1'
    console.error('test', test, copyed)
    
  3. 如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;

    var test = {
        a: function () { console.log('hello world最强写手') },
        b: { c: 5 },
        c: [0, 1, 2],
        d: 'jack',
        e: new Date(),
        f: null,
        g: undefined,
        h: obj
    }
    var clone = function (obj) {
       return JSON.parse(JSON.stringify(obj));
    }
    var b = clone(obj);
    console.log(b); 
    
  4. 如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null.

    var obj = {
       a: function () { console.log('hello world最强写手') },
       b: { c: 5 },
       c: [0, 1, 2],
       d: 'jack',
       e: new Date(),
       f: null,
       g: NaN,
       h: obj
    }
    var clone = function (obj) {
       return JSON.parse(JSON.stringify(obj));
    }
    var b = clone(obj);
    console.log(b);
    
  5. JSON.stringify()只能序列化对象的可枚举的自有属性。//不可枚举是遍历不出来。

  6. 如果对象中存在循环引用的情况也无法正确实现深拷贝。

常规版的深拷贝:

不能解决,正则,date,对象的循环引用的问题。

```
const deepClone = (target, map = new Map()) => {
      if (typeof target === 'object' && target !== null) {
          if(map.has(target)){
              return map.get(target);
          }
          const cloneTarget = Array.isArray(target) ? [] : {};
          map.set(target,cloneTarget);
          if(Array.isArray(target)){
                target.forEach((item,index)=>{
                   cloneTarget[index] = deepClone(item,map);
                })
          }else{
                Object.keys(target).forEach((key)=>{
                    cloneTarget[key] = deepClone(target[key],map);
                })
          }
          return cloneTarget;
         } else {
              return target;
         }
}
```
开发版的深拷贝
function forEach(array, iteratee) {
            let index = -1;
            const length = array.length;
            while (++index < length) {
                iteratee(array[index], index);
            }
            return array;
} 
function isObject(target) {
    const type = typeof target;
    return target !== null && (type === 'object' || type === 'function');
}
function getType(target) {
    return Object.prototype.toString.call(target);
}
 const mapTag = '[object Map]';
 const setTag = '[object Set]';
 const arrayTag = '[object Array]';
 const objectTag = '[object Object]';
​
 const boolTag = '[object Boolean]';
 const dateTag = '[object Date]';
 const errorTag = '[object Error]';
 const numberTag = '[object Number]';
 const regexpTag = '[object RegExp]';
 const stringTag = '[object String]';
 const symbolTag = '[object Symbol]';
 const deepTag = [mapTag, setTag, arrayTag, objectTag]
 function getInit(target) {
     const Ctor = target.constructor;
     return new Ctor();
  }
  function clone(target, map = new WeakMap()) {
            // 克隆原始类型
      if (!isObject(target)) {
          return target;
      }
         // 初始化
      const type = getType(target);
      let cloneTarget;
      if (deepTag.includes(type)) {
                cloneTarget = getInit(target, type);;
      }
       // 防止循环引用
      if (map.get(target)) {
           return map.get(target);
      }
      map.set(target, cloneTarget);
​
      // 克隆set
     if (type === setTag) {
      target.forEach(value => {
           cloneTarget.add(clone(value, map));
       });
           return cloneTarget;
      }
​
      // 克隆map
      if (type === mapTag) {
          target.forEach((value, key) => {
               cloneTarget.set(key, clone(value, map));
           });
              return cloneTarget;
       }
​
       // 克隆对象和数组
      const keys = type === arrayTag ? undefined : Object.keys(target);
          forEach(keys || target, (value, key) => {
          if (keys) {
               key = value;
          }
          cloneTarget[key] = clone(target[key], map);
     });
    return cloneTarget;
}
克隆Symbol
function cloneSymbol(targe) {
   return Object(Symbol.prototype.valueOf.call(targe));
}