使用 javascript 写一个简单的深拷贝

119 阅读3分钟

这是一个非常简易的深拷贝解析

<script>
      //先设置一个对象,用来做拷贝
      let obj = {
        name: 'slimShady',
        age: 45,
        hobby: [
          'rap--说唱',
          'Political preaching--政治宣讲',
          'Attack social phenomena--攻击社会现象'
        ],
        class: {
          name: 'personal rap class',
          income: ['$20,000', '$25,000', '$30,000']
        }
      }
</script>

首先了解一下浅拷贝:

      //浅拷贝 ,拷贝的是地址
      let obj1 = obj //将obj在堆内存中的地址拷贝出来,复制给在栈内存中的obj1
      obj1.name = '5cent' //更改obj1.name后,obj.name也会跟着改变
      console.log(obj.name, obj1.name) // '5cent' '5cent'
      console.log(obj === obj1) //true,此时obj与obj1不仅数据相同,地址也相同,所以输出为true

再来看看深拷贝: 深拷贝简单讲就是只拷贝数据,而不拷贝内存,

在实际开发中实现深拷贝有两种方式

1.简单方式:(实际开发中常用的方式):只需要把js对象先转成JSON,然后再转成js,就可以实现深拷贝。(这种方式会有一些弊端,简单说就是JSON只能遍历对象所有可枚举的属性,而其他的比如对象内的值为构造函数的实例对象或者比如NAN ,INFINITY ,-INFINITY这些属性时会造成数据丢失等等情况,这里不多做赘述)

      
      //初始形态:
      let json = JSON.stringify(obj)//编译器底层会自动深拷贝
      console.log(json)
      let js = JSON.parse(json)//将JSON对象再转换成js格式就可以实现深拷贝
      console.log(js)
      console.log(json === js)//false,此时json与js数据虽然相同但是地址不同,所以输出为false
      
      // 简化形态:
      let obj2 = JSON.parse(JSON.stringify(obj))
      console.log(obj2)
      console.log(obj === obj2)

2.复杂数据使用递归实现深拷贝

2.1 不使用递归时,如果原对象都是简单数据类型,简单思路,遍历原对象,进行赋值:

        function copyRight (newObj, obj) {
          // 先遍历一遍原对象obj的每一个成员,赋值给newObj
          for (let key in obj) {
            newObj[key] = obj[key]
          }
        }
        //然后声明一个新对象存储拷贝后的数据
        let newObj = {}
        //开始深拷贝
        copyRight(newObj, obj)
        console.log(newObj, obj)
        //此时完成了非引用类型数据的深拷贝,但是引用类型数据并未深拷贝 */

2.2 使用递归,可以对对象中的引用数据类型进行遍历拷贝:深层深拷贝

      //遍历原对象,但是针对不同数据类型做不同判断,如数组以及对象类型,先创建新容器,后进行赋值,而值类型数据则作为递归结束条件,实例如下:
      function copyRight (newObj, obj) {
        for (let key in obj) {
          if (obj[key] instanceof Array) {
            newObj[key] = []
            copyRight(newObj[key], obj[key])  **==>在这里递归**
            //数组:引用类型
            // instanceof运算符的作用是用来检测constructor.prototype是否存在于参数object上
            // 即检测某一数据类型的原型是否存在某一数据的原型链上,以此可以判断此数据为那种类型
            // 此时为判断该obj[key]的原型链上是否存在Array数组的原型链,如果有则为数组,对象及值类型同理
          } else if (obj[key] instanceof Object) {
            //对象:引用类型
            newObj[key] = {}
            copyRight(newObj[key], obj[key])  **==>在这里递归**
          } else {
            newObj[key] = obj[key]
            //数值类型终止递归
          }
        }
      }
      let newObj = {} //声明一个空对象存储拷贝后的数据
      copyRight(newObj, obj) //开始深拷贝
      console.log(newObj, obj) //拷贝完成后newObj与obj数据相同但是地址不同
      console.log(newObj === obj) //false//验证正确
    </script>

一个非常简单的深拷贝完成。