深浅拷贝

浅拷贝

手写

遍历对象,把属性及属性值都放在一个新的对象。

function shallowCopy(obj) {
  // 只拷贝对象
  if (typeof obj !== 'object') return

  // 根据 obj 的类型判断是新建一个数组还是对象
  let newObj = Array.isArray(obj) ? [] : {}

  // 遍历 obj,并且判断是 obj 的属性才拷贝
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] = obj[key]
    }
  }

  return newObj
}

扩展运算符

let obj1 = { 
  a: 1, 
  b: { 
    val: 2 
  } 
}

let obj2 = {...obj1}

obj1.a = 3
obj1.b.val = 4

console.log(obj1) // { a: 3, { b: { val: 4 } } }
console.log(obj2) // { a: 1, { b: { val: 4 } } }

Object.assign

let obj1 = { 
  a: 1, 
  b: { 
    val: 2 
  } 
}

let obj2 = Object.assign({}, obj1)

obj1.a = 3
obj1.b.val = 4

console.log(obj1) // { a: 3, { b: { val: 4 } } }
console.log(obj2) // { a: 1, { b: { val: 4 } } }

Array.prototype.slice

let arr1 = [1, { val: 2 }]
let arr2 = arr1.slice()

arr1[0] = 3
arr1[1].val = 4

console.log(arr1) // [3, { val: 4 }]
console.log(arr2) // [1, { val: 4 }]

Array.prototype.concat

let arr1 = [1, { val: 2 }]
let arr2 = arr1.concat()

arr1[0] = 3
arr1[1].val = 4

console.log(arr1) // [3, { val: 4 }]
console.log(arr2) // [1, { val: 4 }]

深拷贝

手写

遍历对象,把属性及属性值放在一个新的对象。当属性值是对象时,递归调用深拷贝函数。

var deepCopy = function(obj) {
  // 只拷贝对象
  if (typeof obj !== 'object') return

  // 根据 obj 的类型判断是新建一个数组还是对象
  var newObj = Array.isArray(obj) ? [] : {}

  // 遍历 obj,并且判断是 obj 的属性才拷贝。属性值是对象时,采用递归。
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]
    }
  }
  
  return newObj
}

WARNING

该方法虽实现了深拷贝,但也存在一些问题。

  • 无法解决循环引用
  • 无法拷贝 symbol 和不可枚举的属性
  • 无法拷贝特殊类型的值,如 DateRegExp

更完善的手写方法,待后续有空时再研究补上。

JSON.parse(JSON.stringify(obj))

let obj1 = { 
  a: 1, 
  b: { 
    val: 2 
  } 
}

let obj2 = JSON.parse(JSON.stringify(obj1))

obj1.a = 3
obj1.b.val = 4

console.log(obj1) // { a: 3, { b: { val: 4 } } }
console.log(obj2) // { a: 1, { b: { val: 2 } } }

WARNING

该方法虽简单粗暴,但也存在一些问题。

  • 无法解决循环引用
  • 无法拷贝对象的原型链
  • 无法拷贝不可枚举的属性
  • 拷贝 Date 引用类型会变成字符串
  • 拷贝 RegExp 引用类型会变成空对象
  • 拷贝 NaN±Infinity 会变成 null
  • 拷贝 undefinedsymbolfunction会消失

lodash.cloneDeep

let obj1 = { 
  a: 1, 
  b: { 
    val: 2 
  } 
}

let obj2 = _.cloneDeep(obj1)

obj1.a = 3
obj1.b.val = 4

console.log(obj1) // { a: 3, { b: { val: 4 } } }
console.log(obj2) // { a: 1, { b: { val: 2 } } }

参考

Last Updated:
Contributors: Vsnoy