深拷贝与浅拷贝

99 阅读3分钟

深拷贝与浅拷贝

深浅拷贝的出现,其实是引用类型的特性导致的;若只是简单数据类型,便都是深拷贝;

由于复杂数据类型的特点:复杂数据类型的赋值,并不是将数据赋给对方,而是将一个引用地址赋给对方,这就会导致,两者的地址,指向的都是同一个堆区,那么此时随便一个变量修改堆区的内容,另一个变量也会随之改变; 例如:

  let a = 1
      let b = a
      b = 2
      console.log(a, b)
      let obj1 = {
        a: 1,
        b: 'zs',
        c: undefined,
        d: { name: '嘻嘻' },
        e: [1, 9],
        func: () => {}
      }
      let obj2 = obj1
      obj2.d.name = '哈哈'
      obj2.e[0] = 6
      obj2.a = 02
      console.log(obj1, obj2)

打印结果:

  • 作为简单数据类型的ab,赋值后不存在一个改变,另一个也改变的情况; 而obj1obj2,则是obj1(或者obj2)中的内容,一个改变,另一个也会随之改变在这里插入图片描述

深拷贝要解决的就是上述问题,让复杂数据类型被拷贝后,也可以像简单数据类型一样。

浅拷贝

  • 对象浅拷贝解决方法一:展开运算符

    let obj1 = {
            a: 1,
            b: 'zs',
            c: undefined,
            d: { name: '嘻嘻' },
            e: [1, 9],
            func: () => {}
          }
          let obj2 = { ...obj1 }
          obj2.d.name = '哈哈'
          obj2.e[0] = 6
          obj2.a = 2
          console.log('obj1:', obj1, 'obj2:', obj2)
    

    在这里插在这里插入图片描述
入图片描述 可以看到,扩展运算符也可以实现第一级元素的深拷贝,第二级以上就只能实现浅拷贝

  • 对象浅拷贝解决方法二:object.assign方法

     let obj1 = {
        a: 1,
        b: 'zs',
        c: undefined,
        d: { name: '嘻嘻' },
        e: [1, 9],
        func: () => {}
      }
      let obj2 = Object.assign({}, obj1)
      obj2.d.name = '哈哈'
      obj2.e[0] = 6
      obj2.a = 2
      console.log('obj1:', obj1, 'obj2:', obj2)

在这里插入图片描述

深拷贝

  • 对象深拷贝解决方法一:JSON

     let obj1 = {
        a: 1,
        b: 'zs',
        c: undefined,
        d: { name: '嘻嘻' },
        e: [1, 9],
        func: () => {}
      }
      let obj2 = JSON.parse(JSON.stringify(obj1))
      obj2.d.name = '哈哈'
      obj2.e[0] = 6
      obj2.a = 2
      console.log('obj1:', obj1, 'obj2:', obj2)
    

    打印的结果为: 在这里插入图片描述

    可以看到,JSON的方式,可以实现深拷贝,但是,有缺点, 缺点:数据类型为undefinedsymbolfunction不能拷贝

  • 对象深拷贝解决方法二:递归(比较完美的方案)

      let obj1 = {
        a: 1,
        b: 'zs',
        c: undefined,
        d: { name: '嘻嘻' },
        e: [1, 9],
        func: () => {}
      }
      function cloneDeep (data) {
        // 判断传进来的是数组还是对象,分别返回对应的空数组和空对象,作为容器
        const newData = Array.isArray(data) ? [] : {}

        for (key in data) {
          if (data[key] && typeof data[key] === 'object') {
            // 进入此处,data[key] 不是对象就是数组,那就调用cloneDeep方法,进行递归!直到data[key] 为一个简单数据类型为止。

            newData[key] = cloneDeep(data[key])
          } else {
            // 来到此处,说明 data[key] 就是简单数据类型,不用进行递归,直接赋值。

            newData[key] = data[key]
          }
        }
        return newData
      }
      let obj2 = cloneDeep(obj1)
      obj2.d.name = '哈哈'
      obj2.e[0] = 6
      obj2.a = 2
      console.log('obj1:', obj1, 'obj2:', obj2)

在这里插入图片描述

数组的浅拷贝,和上面一样,但是数组又多出两个方法

  • 数组方法——slice

          let arr1 = [1, 2, undefined, function () {}, { name: 'ls' }]
          let arr2 = arr1.slice()
          arr2[0] = 8
          arr2[4].name = '李四'
          console.log('arr1:', arr1, 'arr2:', arr2)
    

在这里插入图片描述 同样的,数组的slice方法 也可以实现第一级元素的深拷贝,第二级以上就只能实现浅拷贝

  • 数组方法——concat

       let arr1 = [1, 2, undefined, function () {}, { name: 'ls' }]
          let arr2 = arr1.concat()
          arr2[0] = 8
          arr2[4].name = '李四'
          console.log('arr1:', arr1, 'arr2:', arr2)
    

    在这里插入图片描述 同样的,数组的concat方法 也可以实现第一级元素的深拷贝,第二级以上就只能实现浅拷贝

数组递归方法(数组深拷贝 比较完美的方案)

代码逻辑和上面对象递归一样

    let arr1 = [1, 2, undefined, function () {}, { name: 'ls' }]
      function arrCloneDeep (data) {
        let newArray = Array.isArray(data) ? [] : {}
        for (key in data) {
          if (data[key] && typeof data[key] === 'object') {
            newArray[key] = arrCloneDeep(data[key])
          } else {
            newArray[key] = data[key]
          }
        }
        return newArray
      }
      let arr2 = arrCloneDeep(arr1)
      arr2[0] = 8
      arr2[4].name = '李四'
      console.log('arr1:', arr1, 'arr2:', arr2)

在这里插入图片描述 如上图,完美的深拷贝!

总结

对象浅拷贝解决方案:

  1. JSON转换法
    • 缺点:数据类型为functionundefined时,无法复制
  2. 递归 1. 比较完美解决方案

数组浅拷贝解决方案:

  1. JSON转换法

    • 缺点:数据类型为functionundefined时,无法复制
  2. 递归

    1. 比较完美解决方案