1-9 深拷贝与浅拷贝

25 阅读2分钟

深拷贝和浅拷贝主要是针对对象的属性是对象(引用类型)

  • 浅拷贝:拷贝的是栈中的地址,所以当修改其中的一个,另一个也会修改
  • 深拷贝:拷贝的值一样,但是指向的地址不一样。所以当修改其中一个并不影响另外一个

浅拷贝 Object.assign()

只是一个浅拷贝,对于基本类型可以直接替换,对于引用类型就会有问题

let target = {}
let source =  {
  a: 1,
  b: 2
}

Object.assign(target, source)
console.log(target) // {  a: 1,  b: 2 }

此时我们看到以上代码并没有问题,下面我们把代码写的复杂一些再看一下

let target = {
    a: {
    b: {
      c: 1
    }
    e: 2,
    f: 3
  }
}
let source =  {
  a: {
    b: {
      c: 1
    }
    e: 2
  }
}
Object.assign(target, source)
console.log(target)  // 我们可以看到打印出来的数据 f 丢失了
  • Object.assign 在拷贝对象的时候是有问题的,对于基本数据类型是没有问题的,但是对于引用数据只是把地址直接指过去,如果结构很复杂有些属性会直接丢失,
  • 在使用Object.assign去复制一个比较复杂的结构并不安全,
  • Object.assign 只是一个浅拷贝并不是深拷贝

浅拷贝

obj1 = {
  name: 'UU',
  age: 1
}
let obj2 = obj2
obj1.age = 18
console.log(obj1)
console.log(obj2)
  • 当 obj1 发生变化的时候obj2 也发生了变化,这种叫做浅拷贝
  • 相当于obj1 和obj2 指向了同一块内存地址

深拷贝

let a = 5
let b = a
a = 6
console.log(a, b)
  • 当 a 发生变化的时候 b 并没有发生变化,这个我们可以叫做深拷贝,两个值互相并不影响

下面我们使用 JSON.parse() JSON.stringify() 实现一个深拷贝

obj1 = {
  name: 'UU',
  age: 1
}
let str = JSON.stringify(obj1)
let obj2 = JSON.parse(str)
obj2.age = 18
console.log(obj2) 
  • 我们可以看到 age 还是 1 说明obj1 和obj2 在堆内存里面是两个不同的内存地址,所以他们两个互相不干扰

下面我们封装一个函数来实现以下深拷贝

let checkType = data => {
  return Object.prototype.toString.call(data).slice(8, -1)
}
checkType({})
let deepClone = target => {
  let targetType = checkType(target)
  let result
  if(targetType === 'Object') {
    result = {}
  }else if(targetType === 'Array') {
    result = []
  }else {
    return target
  }
  for(let i in target){
    let value = target[i]
    let valueType = checkType(value)
    if(valueType === 'Object' || valueType === 'Array'){
      result[i] = deepClone(value)
    }else{
      result[i] = value
    }
  }
  return result
}
let arr1 = [1, 2, {age: 18}]
let arr2 = deepClone(arr1)
arr2[2].age = 4
console.log(arr1)