zoukankan      html  css  js  c++  java
  • 浅拷贝(Shallow) VS 深拷贝(Deep)

    首先,深拷贝和浅拷贝针对的是对象类型(对象,数组,函数)。

    浅拷贝指的是只是拷贝了对象的引用地址,彼此之间高耦合,一个改变,另一个可能也随之改变;

    深拷贝是指只是完整的将变量的值拷贝过来,是一个新的对象,和被拷贝对象解耦合,一个改变,不会影响其它的内容。

    浅拷贝

    当对象有多层属性值时,只拷贝第一层, 是浅拷贝。如果被拷贝对象只有一层,下面的方法就是深拷贝。

     1)对象遍历赋值

    let copy = function(target,source){
        for(let property in source){
           target[property] = source[property]
        }     
        return target;   
    }

     2) 对象合并函数Object.assign({})--复制可遍历属性(元属性enumerable为true)

    当Object.assign(obj)只有一个参数时,相当于返回自身。
    let copy = Object.assign(obj); // copy === obj// 注意:当obj的值为原始类型的值(boolean,string,number)时,会将原始值转为包装对象。当obj为undefined或者null时,无法转为对象会报错。
    
    当Object.assign(target, source1, source2,...)有多个参数时,将第一个设为空对象{}
    // 浅拷贝 let copy
    = Object.assign({}, source);
    // 注意: 当source为undefined或者null或者(boolean,number)时,会忽略source;当source为string类型时,会将字符串转为对象 Object.assign({},str) ---> //{0: "a", 1: "b", 2: "c"}

    3)扩展运算符(...)性质同1)

    let a = {
        age: {
    my: 18
    } } let b = {...a};
    b.age.my = 20;
    a; //{age: {my: 20}}

    4) [xxx].slice() 浅拷贝

    var a = [[1,2,3]]; //目标对象是多维数组
    var b = slice();
    b[0][0] = 100;
    a; // [[100,2,3]]

    深拷贝

    为了不出现上面例子中,嵌套关系拷贝只拷贝地址的情况,我们需要遍历到最底层,逐级赋值,实现深拷贝

    1.JSON.parse(JSON.stringify(obj))

      该方法可以解决大多数的深拷贝问题。实际开发任务中,基本都可以使用。但是有些特殊情况:

      具体使用JSON.stringify的特殊的情况如下:

     1)忽略属性值为undefined和function类型和Symbol类型的值;

    JSON.parse(JSON.stringify({a:1, b:function(){}, c:undefined, d: Symbol()}))
    // 结果如下:
    {a: 1}

     2) 值为Regex,Error类型的值会变成空对象{};

    JSON.parse(JSON.stringify({a:1, b: RegExp('a'), c: new Error('err')}));
    // 结果
    {a:1, b: {}, c: {}}

     3) 值NaN,Infinity,-Infinity的值会变成null;

    JSON.parse(JSON.stringify({a:1, b: NaN, c:Infinity, d: -Infinity}))
    // 结果
    {a:1, b: null, c: null, d: null}

     4)不能序列化循环引用的对象,会抛出异常

    var a = {};
    a.b = a;
    JSON.stringify(a); 
    // Uncaught Error: Converting circular structure to JSON

    5)当参数对象有自定义的toJSON()方法时,JSON.stringify()会将该方法的返回值作为最后的参数值,忽然其它的参数。

     

    6)当参数值为Date对象类型时,因为Date对象有toJSON()方法,则JSON.stringify()会将Date类型的值按照字符串转换,

        然后JSON.parse()去解析JSON字符串,最终会解析成字符串,不会解析成Date类型。

       

     2.通用的深拷贝方法

        1)直接使用lodash中的cloneDeep方法

        2)手写一个深拷贝方法

    function deepClone(obj, hash=new WeakMap()) {
      // 如果obj为null/undefined
      if(obj == null){// null == undefined
        return obj;
      }
      if (obj instanceof RegExp) {
        return new RegExp(obj); //拷贝正则对象,需要新生成一个对象
      }
      if (obj instanceof Date) {
        return new Date(obj);
      }
      if(obj instanceof Error) {
        return new Error(obj);
      }
      // 如果是原始类型的值(string/number/boolean/symbol)或者function
      // function不需要被拷贝,使用时是调用
      if(typeof obj !== 'object'){
        return obj;
      }
      // 如果是对象(Array,Object)需要递归拷贝
      if(hash.get(obj)) return hash.get(obj); 
      let cloneObj = new obj.constructor;
      // 防止出现对象属性循环调用自身的情况: var a ={}; a.b= a;
      hash.set(obj, cloneObj);
      for(let key in obj) {
        if (obj.hasOwnProperty(key)) {// 只拷贝自身的属性
          cloneObj[key] = deepClone(obj[key], hash);
        }
      }
      return cloneObj;
    }
    
    //测试
    var a = {a:1, b:function(){}, c:undefined, d: Symbol()};
    var b = {a:1, b: RegExp('a'), c: new Error('err')};
    var c = {a:1, b: NaN, c:Infinity, d: -Infinity};
    var d = {a:1, b: new Date()}
    var e = {}; e.a = e; 
    console.log(deepClone(a)); // {a:1, b:function(){}, c:undefined, d: Symbol()};
    console.log(deepClone(b)); // {a:1, b: RegExp('a'), c: new Error('err')};
    console.log(deepClone(b)); // {a:1, b: NaN, c:Infinity, d: -Infinity};
    console.log(deepClone(d)); // {a:1, b: new Date()}
    console.log(deepClone(e)); // {a: [Circular]}
  • 相关阅读:
    [PHP] class_exists类不存在时会调用__autoload函数
    [Redis] Redis的消息机制- 发布订阅
    [发电] 现在正式入驻爱发电平台
    [MySQL] PHP IP登录限制的实现
    [Redis] 哈希表的Rehash机制
    [Redis] redis的hash类型底层结构哈希表
    [Linux] ls命令的几个常用参数实现按时间/文件大小排序
    [Go] 在gin框架gorm下查询一对多的数据
    [Redis] list底层的数据结构
    [GO]go redis实现滑动窗口限流-redis版
  • 原文地址:https://www.cnblogs.com/lyraLee/p/10697055.html
Copyright © 2011-2022 走看看