美文网首页
JavaScript深浅拷贝,其实没那么难!

JavaScript深浅拷贝,其实没那么难!

作者: 越前君 | 来源:发表于2020-03-03 15:38 被阅读0次

    本文更新于 2020-03-03。

    什么是深浅拷贝?
    • 浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

    • 深拷贝:将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

    浅拷贝的几种方式

    array.slice()array.concat()Object.assign() 都能实现浅拷贝,前两个针对数组。还可以用以下这种方式。

    // 浅拷贝
    function shallowCopy(source, target = {}) {
      for (let key in source) {
        if(source.hasOwnProperty(key)) {
          target[key] = source[key]
        }
      }
      return target;
    }
    
    let student1 = {
      name: "Frankie",
      age: 22,
      language: ["Chinese", ["English", "German"]]
    };
    
    let student2 = shallowCopy(student1);
    student2.language[1] = ["French"];
    
    console.log(student1);
    console.log(student2);
    // 打印对比可知,language 同时改变了
    
    深拷贝的几种方式
    • JSON
      利用 JSON.stringify & JSON.parse 方法

    不足:

    1. 无法拷贝对象中的方法属性;
    2. 无法拷贝对象中的 undefined 属性
    • JQuery
      JQuery 提供的两种方法 $.extend()$.clone() ,前者是 JS 对象的拷贝、后者是 DOM 对象的拷贝(这里不讨论)

    不足:

    1. 需要引入 JQuery 库
    2. 无法拷贝对象中的 undefined 属性
    • 递归
      利用 for..in..hasOwnProperty递归实现

    不足:比上面的两种方式稍复杂

    递归实现深拷贝

    // 假设在 Object 的原型,添加一个 height 属性
    Object.prototype.height = 180;
    
    // student1 什么类型的属性都有了
    let student1 = {
      name: "Frankie",
      age: 22,
      private: true,
      friends: ["Mandy", "John"],
      abilities: undefined,
      other: null,
      car: {
        color: "gray",
        brand: "Benz"
      },
      teacher: {
        name: "Ada",
        student: ["Helkai", "Jerry"]
      },
      learn: function() {
        console.log(this.name + " is learning JavaScript now.");
      },
    };
    
    // 深拷贝
    function deepCopy(source, target = {}) {
      for (let key in source) {
        // for...in... 会遍历原型链上的属性,所以这里需要利用 hasOwnProperty 判断,否则会把 Object 原型上的 height 也拷贝进去了
        if (source.hasOwnProperty(key)) {
          if (typeof source[key] === "object" && source[key] !== null) {
            // 判断是否为数组
            target[key] = Object.prototype.toString.call(source[key]) === "[object Array]" ? [] : {};
            // 递归
            deepCopy(source[key], target[key]);
          } else {
            target[key] = source[key];
          }
        }
      }
      return target;
    }
    
    // 从 student1 中拷贝一个 student2 出来
    let student2 = deepCopy(student1);
    // 修改 student2 的属性值
    student2.name = "Steven";
    student2.age = 20;
    student2.car = {
      color: "red",
      brand: "BMW"
    };
    student2.friends[1] = "Jone";
    student2.teacher.age = "30";
    // 打印结果看下面截图,结果是没有相互影响的
    console.log(student1);
    console.log(student2);
    
    // JSON 方式
    let student3 = JSON.parse(JSON.stringify(student1));
    console.log(student3);  //  打印 student3 可见 abilities、learn 属性丢失了
    

    运行结果

    日常搬砖选择哪种方式呢,视具体情况而定。我的话,能用 JSON 方式解决的,绝不用递归,没必要复杂化,简单够用即可。但是学习的话,递归的方式必须得懂啊,其实不难懂。

    其实还有优化的空间,我们都知道 for...in... 的性能是比较差的。关于性能优化篇,可以看下掘金某大神的文章,这里

    The end.

    相关文章

      网友评论

          本文标题:JavaScript深浅拷贝,其实没那么难!

          本文链接:https://www.haomeiwen.com/subject/lybflhtx.html