• [转] js深拷贝和浅拷贝


    一、数组的深浅拷贝

    在使用JavaScript对数组进行操作的时候,我们经常需要将数组进行备份,事实证明如果只是简单的将它赋予其他变量,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致了问题的发生。

    var arr = ["One","Two","Three"];
    
    var arrto = arr;
    arrto[1] = "test";
    document.writeln("数组的原始值:" + arr + "<br />");//Export:数组的原始值:One,test,Three
    document.writeln("数组的新值:" + arrto + "<br />");//Export:数组的新值:One,test,Three

    像上面的这种直接赋值的方式就是浅拷贝,很多时候,这样并不是我们想要得到的结果,其实我们想要的是arr的值不变,不是吗? 

    方法一:js的slice函数 

    复制代码
    对于array对象的slice函数,
    返回一个数组的一段。(仍为数组)
    arrayObj.slice(start, [end])
    参数
    arrayObj
    必选项。一个 Array 对象。
    start
    必选项。arrayObj 中所指定的部分的开始元素是从零开始计算的下标。
    end
    可选项。arrayObj 中所指定的部分的结束元素是从零开始计算的下标。
    说明
    slice 方法返回一个 Array 对象,其中包含了 arrayObj 的指定部分。
    slice 方法一直复制到 end 所指定的元素,但是不包括该元素。如果 start 为负,将它作为 length + start处理,此处 length 为数组的长度。如果 end 为负,就将它作为 length + end 处理,此处 length 为数组的长度。如果省略 end ,那么 slice 方法将一直复制到 arrayObj 的结尾。如果 end 出现在 start 之前,不复制任何元素到新数组中。
    复制代码
    var arr = ["One","Two","Three"];
    
    var arrtoo = arr.slice(0);
    arrtoo[1] = "set Map";
    document.writeln("数组的原始值:" + arr + "<br />");//Export:数组的原始值:One,Two,Three
    document.writeln("数组的新值:" + arrtoo + "<br />");//Export:数组的新值:One,set Map,Three

    方法二:js的concat方法 

    concat() 方法用于连接两个或多个数组。
    该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
    语法
    arrayObject.concat(arrayX,arrayX,......,arrayX)
    说明
    返回一个新的数组。该数组是通过把所有 arrayX 参数添加到 arrayObject 中生成的。如果要进行 concat() 操作的参数是数组,那么添加的是数组中的元素,而不是数组。
    var arr = ["One","Two","Three"];
    
    var arrtooo = arr.concat();
    arrtooo[1] = "set Map To";
    document.writeln("数组的原始值:" + arr + "<br />");//Export:数组的原始值:One,Two,Three
    document.writeln("数组的新值:" + arrtooo + "<br />");//Export:数组的新值:One,set Map To,Three

    二、对象的深浅拷贝

    var a={name:'yy',age:26};
    var b=new Object();
    
    b.name=a.name;
    b.age=a.age;
    a.name='xx';
    console.log(b);//Object { name="yy", age=26}
    console.log(a);//Object { name="xx", age=26}

    就是把对象的属性遍历一遍,赋给一个新的对象。

    var deepCopy= function(source) { 
    var result={};
    for (var key in source) {
          result[key] = typeof source[key]===’object’? deepCoyp(source[key]): source[key];
       } 
       return result; 
    }

    2016-11-26

    ---------------------------------------------------------------------------------------------------------------------------------------------------

    2019-09-26

    深克隆方法

    方法一

    const newObj = JSON.parse(JSON.stringify(oldObj));

    这个方法虽然可以解决绝大部分是使用场景,但是却有很多坑。

    1. 他无法实现对函数 、RegExp等特殊对象的克隆

    2. 会抛弃对象的constructor,所有的构造函数会指向Object

    3. 对象有循环引用,会报错

    // 构造函数
    function person(pname) {
      this.name = pname;
    }
    
    const Messi = new person('Messi');
    // 函数
    function say() {
      console.log('hi');
    };
    
    const oldObj = {
      a: say,
      b: new Array(1),
      c: new RegExp('ab+c', 'i'),
      d: Messi
    };
    
    const newObj = JSON.parse(JSON.stringify(oldObj));
    
    // 无法复制函数
    console.log(newObj.a, oldObj.a); // undefined [Function: say]
    // 稀疏数组复制错误
    console.log(newObj.b[0], oldObj.b[0]); // null undefined
    // 无法复制正则对象
    console.log(newObj.c, oldObj.c); // {} /ab+c/i
    // 构造函数指向错误
    console.log(newObj.d.constructor, oldObj.d.constructor); // [Function: Object] [Function: person]

    面试版深克隆

    /**
     * deep clone
     * @param  {[type]} parent object 需要进行克隆的对象
     * @return {[type]}        深克隆后的对象
     */
    const clone = parent => {
      // 判断类型
      const isType = (obj, type) => {
        if (typeof obj !== "object") return false;
        const typeString = Object.prototype.toString.call(obj);
        let flag;
        switch (type) {
          case "Array":
            flag = typeString === "[object Array]";
            break;
          case "Date":
            flag = typeString === "[object Date]";
            break;
          case "RegExp":
            flag = typeString === "[object RegExp]";
            break;
          default:
            flag = false;
        }
        return flag;
      };
    
      // 处理正则
      const getRegExp = re => {
        var flags = "";
        if (re.global) flags += "g";
        if (re.ignoreCase) flags += "i";
        if (re.multiline) flags += "m";
        return flags;
      };
      // 维护两个储存循环引用的数组
      const parents = [];
      const children = [];
    
      const _clone = parent => {
        if (parent === null) return null;
        if (typeof parent !== "object") return parent;
    
        let child, proto;
    
        if (isType(parent, "Array")) {
          // 对数组做特殊处理
          child = [];
        } else if (isType(parent, "RegExp")) {
          // 对正则对象做特殊处理
          child = new RegExp(parent.source, getRegExp(parent));
          if (parent.lastIndex) child.lastIndex = parent.lastIndex;
        } else if (isType(parent, "Date")) {
          // 对Date对象做特殊处理
          child = new Date(parent.getTime());
        } else {
          // 处理对象原型
          proto = Object.getPrototypeOf(parent);
          // 利用Object.create切断原型链
          child = Object.create(proto);
        }
    
        // 处理循环引用
        const index = parents.indexOf(parent);
    
        if (index != -1) {
          // 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
          return children[index];
        }
        parents.push(parent);
        children.push(child);
    
        for (let i in parent) {
          // 递归
          child[i] = _clone(parent[i]);
        }
    
        return child;
      };
      return _clone(parent);
    };

    这个深克隆还不算完美,例如Buffer对象、Promise、Set、Map可能都需要我们做特殊处理,另外对于确保没有循环引用的对象,我们可以省去对循环引用的特殊处理,因为这很消耗时间,不过一个基本的深克隆函数我们已经实现了。

  • 相关阅读:
    NOIP2020 游记
    李超线段树
    选举「elections」
    Alt+数字输入
    素数
    CSP-S2020 爆炸记
    [CF487C] Prefix Product Sequence
    [CF489E] Hiking
    L2-019 悄悄关注 (25 分)
    L2-032 彩虹瓶 (25 分)
  • 原文地址:https://www.cnblogs.com/kewenxin/p/6105444.html
Copyright © 2020-2023  润新知