美文网首页
JS 深浅拷贝

JS 深浅拷贝

作者: lazy_tomato | 来源:发表于2020-09-20 17:46 被阅读0次

    START

    • 番茄我又又又来写点啥啦。
    • 最近敲代码,总是遇到需要修改数据,在前端展示。但是最后并不想修改原数据的情况。所以,需要涉及到数据的拷贝了,数据拷贝又分深拷贝,浅拷贝。所以写一篇文章记录一下。
    • 本文仅个人的想法,能力有限,参考即可,后续对这个有更深入的理解了,再补充。
    • 啰嗦一句,深拷贝浅拷贝 和 深克隆浅克隆,其实都差不多一个意思啦,看个人习惯如何称呼。

    JS 的数据类型

    在说深浅克隆之前,我想提一下JS的数据类型。

    • JS数据类型,可以大致分为两种:基本数据类型引用数据类型

    • 基本数据类型:

    1. Undefined
    2. Null
    3. Boolean
    4. Number
    5. String
    • 引用数据类型:
    1. object
    • 当然 ES6 引入了一种新的数据类型 Symbol (这里暂时不讨论 Symbol)

    为什么要这样区分数据类型呢?

    • 因为基本数据类型数据,保存在栈内存
    • 引用类型保存在堆内存中

    那么为什么要分两种保存方式呢?

    • 根本原因是在于保存在栈内存的必须是大小固定的数据,引用的类型的大小不固定,只能保存在堆内存中。但是我们可以将引用类型的地址存储在栈内存中。
    • 文字不好理解的话,举个例子:

      // 1.定义一个基本数据类型--数字类型的变量
      var a = 1
      
      // 定义一个引用类型的变量
      var obj = {
          name: "lazy_tomato"
      }
      
    • 上面代码执行后,在内存中其实就是这样的。

      堆栈内存说明.png

    知道js变量的保存方式有什么用呢?

    • 看一看简单的题目就知道了。

    题目一

    var a = 1
    
    var b = a
    
    console.log('变量a为:', a, "变量b为:", b);  //变量a为: 1 变量b为: 1
    
    a = 88
    
    console.log('变量a为:', a, "变量b为:", b);   //变量a为: 88 变量b为: 1
    

    题目二

    var a = {
        name: '张三',
        age: 14
    }
    
    var b = a
    
    console.log('变量a为:', a, "变量b为:", b);  
    //变量a为: { name: '张三', age: 14 } 变量b为: { name: '张三', age: 14 }
    
    a.name = 'lazy_tomato'
    
    console.log('变量a为:', a, "变量b为:", b);   
    //变量a为: { name: 'lazy_tomato', age: 14 } 变量b为: { name: 'lazy_tomato', age: 14 }
    
    • 题目一中,为什么我修改a,不会修改b ?

    • 题目二中,为什么我修改a,会同时修改b ?

      正是因为题目二中,我们 var b = a 做的操作就是:

      声明一个变量b,b栈内存中存储的地址,和a一样,都指向

      {
      name: '张三',
      age: 14
      }

      所以 a.name = 'lazy_tomato' 执行后 a指向堆内存中为:

      {
      name: 'lazy_tomato',
      age: 14
      }

      但是 b栈内存中存储的地址也指向这个对象,所以 修改a,会同时修改b

    浅拷贝

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

    • 看不懂定义?那就简单点说: 就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力。

    • 题目二,其实就是属于浅拷贝,修改了变量a,变量b也跟着改变。

    浅拷贝实现方式

    1. 可以通过简单的赋值实现

      类似上面的例子

    2. 利用 Object.assign()实现

    Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

    var obj = { a: { a: "hello", b: 21 } };
    
    var newObj = Object.assign({}, obj);
    
    console.log("obj变量为:", obj, "newObj: ", newObj)
    //obj变量为: { a: { a: 'hello', b: 21 } } newObj:  { a: { a: 'hello', b: 21 } }
    
    newObj.a.a = "lazy_tomato";
    
    console.log("obj变量为:", obj, "newObj: ", newObj)
    //obj变量为: { a: { a: 'lazy_tomato', b: 21 } } newObj:  { a: { a: 'lazy_tomato', b: 21 } }
    

    注意:当object只有一层的时候,是深拷贝,例如如下:

    var obj1 = { a: 10, b: 20, c: 30 };
    var obj2 = Object.assign({}, obj1);
    
    console.log(obj1, obj2); 
    //{ a: 10, b: 20, c: 30 } { a: 10, b: 20, c: 30 }
    
    obj1.b = 100;
    console.log(obj1, obj2); 
    //{ a: 10, b: 100, c: 30 } { a: 10, b: 20, c: 30 }
    

    深拷贝

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

    深拷贝实现方式

    1、对象只有一层的话可以使用上面的:Object.assign()函数
    2、转成 JSON 再转回来
    var obj1 = { body: { a: 10 } };
    var obj2 = JSON.parse(JSON.stringify(obj1));
    console.log(obj1, obj2)  //{ body: { a: 10 } } { body: { a: 10 } }
    obj1.body.a = 20;
    console.log(obj1, obj2) //{ body: { a: 20 } } { body: { a: 10 } }
    
    

    缺点

    • 拷贝对象的值,如果存在函数、undefined、symbol,在经过JSON.stringify()序列化后的JSON字符串中这个键值对会消失;
    • 没办法拷贝不可枚举的属性,也不像for in 那样可以拷贝原型上的数据;
    • 拷贝Date引用类型会变成字符串
    • 拷贝RegExp 、Error 引用类型会变成 空对象
    • 对象中含有NaN,Infinity和-Infinity,则序列化的结果会变成null
    3、递归拷贝
    • 写个函数实现递归拷贝
    function deepClone(initalObj, finalObj) {    
        var obj = finalObj || {};    
        for (var i in initalObj) {        
          var prop = initalObj[i];        
          if(prop === obj) {            
            continue;
          }        
          if (typeof prop === 'object') {
            obj[i] = (prop.constructor === Array) ? [] : {};            
            arguments.callee(prop, obj[i]);
          } else {
            obj[i] = prop;
          }
        }    
        return obj;
      }
    

    参考的相关网站博客

    END

    • 写着写着,发现自己还有好多不懂的呀,o(╥﹏╥)o,挂出的相关博客都很棒,推荐看看。加油啦~~ 后续对js更加精通了,再来补充这篇文章。
    • 个人记录,参考即可。

    相关文章

      网友评论

          本文标题:JS 深浅拷贝

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