深拷贝与浅拷贝(解决实际问题)

60 阅读3分钟

什么是深拷贝?什么是浅拷贝?我相信在赋值的时候,很容易出现与深拷贝和浅拷贝相关的问题,这篇文章将从问题引入、深拷贝与浅拷贝的概念、手写深拷贝三个方面来进行。

1.问题引入

在做一个后台管理系统的时候,需要对某个属性进行修改,直接上图。

image.png 点击图片中的修改按钮后,通过以下这个进行修改的函数

// 修改属性
    updateAttr(row) {
      this.tableVisible = false
      //直接赋值,浅拷贝
      this.list = row
    },

弹出的对应的表格如图:

image.png 可以看出,达到了修改所具备的条件,即将需要修改的值都显示出来,但是在没有点击保存的时候,就是没有修改,那么所呈现的值也是不会改变的,结果却是背道而驰,直接上图演示。

修改属性,但是没有点击保存,点击的是取消!取消!取消!

image.png

结果显示

image.png

它这就给我改了!!!

原因是我的list是这样的。

 list: {
        attrName: '',
        attrValueList: [
          // {
          //   attrId: 0,
          //   valueName: '' 这就是对应'安卓手机'和'苹果手机'的
          // }
        ],
        categoryId: 0,
        categoryLevel: 3
      }

list本就是一个对象,里面存在数组,数组里面又有对象,艾玛,真是个深渊。。。如果说list没有这么复杂(多层嵌套)的话,是可以扩展运算符(只能解决一层)来解决的,但是这是不可以的,不信?直接展示。

updateAttr(row) {
      this.tableVisible = false
      console.log(row)
      // 这样直接赋值是不可以的,直接就将数据提交上去了
      // this.list = row
      this.list = { ...row }
    },

图片就不放上来了,反正就是不行,跟前面也是一样的😄😄

这不,知道深拷贝的重要性了吧~~~

2.深拷贝与浅拷贝

存在深拷贝与浅拷贝之说的主要原因就是基本数据类型和复杂数据类型在内存中的存储是有区别的,大家形象的从内存中划分出了栈区堆区

(1)对于基本数据类型来说,它们直接存储于栈区,存储的是数据

(2)对于复杂数据类型来说,它们的数据是存储与堆区的,而栈区中只是存储它们在堆区的地址,故又叫引用数据类型,引用类型实质就是地址类型。

(4)一般来说,赋值就是在拷贝,基本数据类型不存在浅拷贝与深拷贝之说,它们直接拷贝的就是数据

(5)对于引用数据类型来说,就有浅拷贝与深拷贝之分,浅拷贝拷贝的只是地址,而深拷贝拷贝的是数据

(6)不难想象,浅拷贝与深拷贝的区别就来了。

注:以上讲诉的不完善,可能会存在理解不充分的问题,仅个人理解😜😜

3.手写深拷贝

3.1写前补充

(1)在写深拷贝的时候,首先就需要进行数据类型的判断,例如是字符串或数字类型这些,就不存在浅拷贝和深拷贝,直接赋值就可以。

基本数据类型(简单数据类型):

  • Null
  • Undefined
  • String
  • Number
  • Boolean
  • Symbol(Es6新增)
  • BigInt(Es6新增)

引用数据类型(复杂数据类型):Object、Array、Reg、Date、Function等

(2)在写深拷贝中,使用了两种判断数据类型的方法:typeof和instansof

  • typeof:常用于判断基本数据类型
    console.log(typeof [])  // object
    console.log(typeof {})  // object
  • instanceof:主要用来区分引用数据类型
   let obj=[]
   console.log(obj instanceof Object) //false
   consloe.log(obj instanceof Array)  //true

3.2深拷贝代码

// 深拷贝 向外暴露
export function deepClone(obj) {
  // 如果赋值的不是对象和数组类型,或者为null,那么就直接返回(直接赋值)
  if (typeof obj !== 'object' || obj == null) {
    return obj
  }
  // 临时存放处
  let result = null
  if (obj instanceof Array) {
    // 数组
    result = []
  } else {
    // 对象
    result = {}
  }
  // 循环 
  for (let key in obj) {
    // 只是这样的话是不够的,只能解决一层的问题,不能解决多层的问题
    // result[key] = obj[key]
    // 可以使用递归,将对象里面的对象同样也给遍历
    result[key] = deepClone(obj[key])
  }
  return result
}