JavaScript中的浅拷贝与深拷贝(对象复制)

107 阅读4分钟

前言

  • 在谈论浅拷贝和深拷贝之前,先复习一下JS数据类型在赋值方面的知识。
    • 基本数据类型(值类型):基本数据类型的变量和值都是存放在栈内存中,声明之后会分配一块内存区域,基本数据类型之间的赋值是直接把栈内存中存的值赋值给变量(传值)。也就是说两个变量只是占用的空间大小相同,值相同,但是存储的位置不同。(可以理解为重新开辟了一个新的内存空间) 因此,当其中一个值改变的时候,不会对另一个值有影响。
    •       // 案例如下
            var a=10;
            var b=a;
      
    • image.png
    • 引用类型(对象类型):对于引用类型,变量依然保存在栈内存中。而值是保存在堆内存中,当一个变量指向另一个变量时,它们其实指向的是同一个内存空间,因为变量保存的是指向实际对象的指针。从一个变量向另一个变量复制引用类型的值,复制的其实是指针地址而已,因此两个变量最终都指向同一个对象
    • 如图:
    • image.png

浅拷贝和深拷贝

  • 首先,浅拷贝和神拷贝指的是对象类型之间的赋值操作,基本数据类型之间的赋值某种程度上就是一种深拷贝。
  • 两者之间的区别:浅拷贝就是只拷贝了对象在堆内存中的地址,但实际a,b两者指向的是同一块内存空间。而深拷贝则是在堆内存中开辟了一块新空间,但里面存放的内容是一样的,这样改变其中一个对象的值就不会影响到另一个对象了。
  • 一般情况下,对象的等号赋值,函数的传参,都是浅拷贝,也就是只拷贝了数据的地址。

如何实现深拷贝?

1. 利用JS中对JSON的解析方法

  * 什么是JSON?  
      JSON( JavaScript Object Notation) 是一种轻量级的存储和传输数据的格式。经常在数据从服务器发送到网页时使用。
  * JSON的两个方法:
      1. JSON.stringify(value)  方法用于将 JavaScript 值转换为 JSON 字符串,并返回该字符串。
      2. JSON.parse(value)      用于将一个 JSON 字符串转换为对象 并返回该对象。
  * 注意:这两个方法都是会返回一个新的字符串或者对象。
  * 例子:
      ```javascript
          var a = {
              name: "ken",
              age: 18
          }
          var b = a;
          console.log(a === b); // true 直接赋值操作,典型的浅拷贝。
          ------------------------------------------------------
          var a = {
              name: "ken",
              age: 18
          }
          var b = JSON.parse(JSON.stringify(a)); // 先将a转换为字符串,再转换回对象。
          console.log(a === b); // false 此时两个指向的不是同一个对象,所以为false,达到了深拷贝目的。
      ```
  * 缺陷:受json数据的限制,无法拷贝函数,undefined,NaN属性。

2. 函数递归方式

    //代码分析:  形参obj 代表被拷贝目标,  调用函数 传入拷贝目标,
        //      通过Array.isArray(obj)判断obj的类型,result根据obj类型定义自身是数组还是对象。
        //      通过 for in 遍历拷贝目标,
        //      使用 typeof 判断其每一个元素或者属性, 是否为obj类型(typeof  Array/Object  返回值皆为object)
        //          若该属性/元素, 部位null 并且 typeof返回值为object,  则代表其为复杂数据类型, 递归调用 deepCopy(obj[key]),继续拷贝其内部
        //          否则: 代表该元素非 数组  非对象, 为基本数据类型/函数 等  , 直接赋值拷贝即可
        //      最后返回拷贝完成的result ,函数执行完毕
        function deepCopy(obj) {
            var result = Array.isArray(obj) ? [] : {};
            for (var key in obj) {
                if (typeof obj[key] === 'object' && obj[key] !== null) {
                    result[key] = deepCopy(obj[key]); //递归复制
                } else {
                    result[key] = obj[key];
                }
            }
            return result;
        }

3. 使用函数库lodash中的cloneDeep()方法

cloneDeep 作用是将变量数据中所有的值,都依次拷贝一份新的出来,包括但不限于 arraysarray buffersbooleansDatemapsnumbersObjectregexessetsstringssymbolstyped arrays。注意只会拷贝对象的可枚举属性。如果对象不可拷贝,比如是 ErrorFunctionDOMWeakMap,那么返回空对象。