• JS实现深拷贝


    目录

    • JS实现
      • 简单深拷贝(一层浅拷贝)
      • 粗暴深拷贝(抛弃对象的constructor)
      • 复杂深拷贝(相对完美)
    • ES实现
      • lodash中的cloneDeep方法(完美)

    一.JS实现

    1.简单深拷贝(一层浅拷贝)

    ①for循环拷贝

    // 只复制第一层的浅拷贝
    function simpleCopy(obj1) {
       var obj2 = Array.isArray(obj1) ? [] : {};
       for (let i in obj1) {
       obj2[i] = obj1[i];
      }
       return obj2;
    }
    var obj1 = {
       a: 1,
       b: 2,
       c: {
       d: 3
      }
    }
    var obj2 = simpleCopy(obj1);
    obj2.a = 3;
    obj2.c.d = 4;
    alert(obj1.a); // 1
    alert(obj2.a); // 3
    alert(obj1.c.d); // 4
    alert(obj2.c.d); // 4
    

    ②Object.assign()实现一层深拷贝

     var obj1 = {
        a: 1,
        b: 2,
        c: 3
    }
    var obj2 = Object.assign({}, obj1);
    obj2.b = 5;
    console.log(obj1.b); // 2
    console.log(obj2.b); // 5
    
     var obj1 = {
        a: 1,
        b: 2,
        c: ['a','b','c']
    }
    var obj2 = Object.assign({}, obj1);
    obj2.c[1] = 5;
    console.log(obj1.c); // ["a", 5, "c"]
    console.log(obj2.c); // ["a", 5, "c"]
    

    ③slice实现

    // 对只有一级属性值的数组对象使用slice
    var a = [1,2,3,4];
    var b = a.slice();
    b[0] = 2;
    alert(a); // 1,2,3,4
    alert(b); // 2,2,3,4
    
    // 对有多层属性的数组对象使用slice
    var a = [1,[1,2],3,4];
    var b = a.slice();
    b[1][0] = 2;
    alert(a); // 1,2,2,3,4
    alert(b); // 1,2,2,3,4
    

    ④使用concat()方法

     var a=[1,2,[3,4]]
     var c=[];
     var b=c.concat(a);
     b[0]=5;
     b[2][0]=6;
     console.log(b[0]);//5
     console.log(a[0])//1
     console.log(b[2][0]);//6
     console.log(a[2][0])//6
    

    ⑤es6的扩展运算符"..."

     var a=[1,2,[3,4]]
     var b=[...a];
     b[0]=5;
     b[2][0]=6
     console.log(b[0]);//5
     console.log(a[0])//1
     console.log(b[2][0]);//6
     console.log(a[2][0])//6
    

    通过Object.create()实现

    function deepCopy(obj) {
      var copy = Object.create(Object.getPrototypeOf(obj));
      var propNames = Object.getOwnPropertyNames(obj);
      
      propNames.forEach(function(name) {
        var desc = Object.getOwnPropertyDescriptor(obj, name);
        Object.defineProperty(copy, name, desc);
      });
      
      return copy;
    }
    
    var obj1 = { a: 1, b: {bc: 50, dc: 100, be: {bea: 1}} };
    var obj2 = deepCopy(obj1);
    obj2.a = 20;
    obj2.b.bc = 60;
    console.log(obj1.a)//1
    console.log(obj2.a)//20
    console.log(obj1.b.bc)//60
    console.log(obj2.b.bc)//60
    

    2.粗暴深拷贝(抛弃对象的constructor)

    使用JSON.stringify和JSON.parse实现深拷贝:JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象;

    function deepCopy(obj1){
        let _obj = JSON.stringify(obj1);
        let obj2 = JSON.parse(_obj);
        return obj2;
    }
    var a = [1, [1, 2], 3, 4];
    var b = deepCopy(a);
    b[1][0] = 2;
    alert(a); // 1,1,2,3,4
    alert(b); // 2,2,2,3,4
    

    缺陷:它会抛弃对象的constructor,深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object;这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON;

    let obj1 = {
       fun:function(){
          alert(123);
       }
    }
    let obj2 = JSON.parse(JSON.stringify(obj1));
    console.log(typeof obj1.fun); // function
    console.log(typeof obj2.fun); // undefined
    

    3.复杂深拷贝(相对完美)

    递归拷贝实现深拷贝,解决循环引用问题

    /**
     * 判断是否是基本数据类型
     * @param value 
     */
    function isPrimitive(value){
      return (typeof value === 'string' || 
      typeof value === 'number' || 
      typeof value === 'symbol' ||
      typeof value === 'boolean')
    }
    
    /**
     * 判断是否是一个js对象
     * @param value 
     */
    function isObject(value){
      return Object.prototype.toString.call(value) === "[object Object]"
    }
    
    /**
     * 深拷贝一个值
     * @param value 
     */
    function cloneDeep(value){
    
      // 记录被拷贝的值,避免循环引用的出现
      let memo = {};
    
      function baseClone(value){
        let res;
        // 如果是基本数据类型,则直接返回
        if(isPrimitive(value)){
          return value;
        // 如果是引用数据类型,我们浅拷贝一个新值来代替原来的值
        }else if(Array.isArray(value)){
          res = [...value];
        }else if(isObject(value)){
          res = {...value};
        }
    
        // 检测我们浅拷贝的这个对象的属性值有没有是引用数据类型。如果是,则递归拷贝
        Reflect.ownKeys(res).forEach(key=>{
          if(typeof res[key] === "object" && res[key]!== null){
            //此处我们用memo来记录已经被拷贝过的引用地址。以此来解决循环引用的问题
            if(memo[res[key]]){
              res[key] = memo[res[key]];
            }else{
              memo[res[key]] = res[key];
              res[key] = baseClone(res[key])
            }
          }
        })
        return res;  
      }
    
      return baseClone(value)
    }
    

    二.ES插件lodash

    import lodash from 'lodash'
    
    var objects = [1,{ 'a': 1 }, { 'b': 2 }]; 
    var deep = lodash.cloneDeep(objects);
    deep[0] = 2;
    deep[1].a = 2;
    console.log(objects[0]);//1
    console.log(deep[0]);//2
    console.log(objects[1].a);//1
    console.log(objects[1].a);//2
    

    站在巨人肩膀上摘苹果

    https://www.jianshu.com/p/cf1e9d7e94fb

    https://www.jianshu.com/p/5f6cd3dabc1c

    https://segmentfault.com/a/1190000015455662

    https://segmentfault.com/a/1190000018371840

    https://www.jianshu.com/p/f4329eb1bace

    https://www.lodashjs.com/docs/lodash

  • 相关阅读:
    js node 操作
    深入认识javascript中的eval函数
    连接到网页objectivec
    web.config的数据库连接字符串进行加密
    fontsize和font标签的size属性的区别
    js改变背景图片
    MS:Chart:Series 成員 饼图 文字Label 显示在饼外
    读取plist文件
    js判断undefined类型
    当用updatepanel和scriptmanager时,弹出框
  • 原文地址:https://www.cnblogs.com/eternityz/p/13645606.html
Copyright © 2020-2023  润新知