浅拷贝 shallow clone

Object.assign

  1. let target = {}
  2. let source = {
  3. a : {
  4. b : 2
  5. }
  6. }
  7. Object.assign(target, source)
  8. console.log(target)
  9. target.a.b = 10
  10. console.log(source)

它不会拷贝对象的继承属性、对象的不可枚举的属性
它可以拷贝Symbol类型的属性

到了对象的第二层属性target.a.b这里的时候, 前者值的改变也会影响后者的第二层属性的值,说明其中依旧存在着访问共同堆内存的问题,也就是说这种方法还不能进一步复制,而只是完成了浅拷贝的功能

扩展运算符spread operator

let obj = {a:1,b:{c:1}}
let obj1 = {...obj}
obj1.a = 2
console.log(obj)
obj1.b.c = 2
console.log(obj)
/* 数组的拷贝 */
let arr = [1, 2, 3];
let newArr = [...arr];

扩展运算符 和 object.assign 有同样的缺陷,也就是实现的浅拷贝的功能差不多,但是如果属性都是基本类型的值,使用扩展运算符进行浅拷贝会更加方便。

concat

let arr = [1, 2, 3]
let newArr = arr.concat()
newArr[1] = 1000
console.log(arr, newArr)

slice

let arr = [1, 2, {val: 4}];
let newArr = arr.slice();
newArr[2].val = 1000;
console.log(arr);  //[ 1, 2, { val: 1000 }

从上面的代码中可以看出,这就是浅拷贝的限制所在了—它只能拷贝一层对象,如果存在对象的嵌套,那么浅拷贝将无能为力

实现浅拷贝

const shallowClone = (obj) => {
  if (typeof obj === 'object' && obj != null) {
    const cloneObj = Array.isArray() ? [] : {}
    for (let prop in obj) {
      if (obj.hasOwnProperty(prop)) {
        cloneObj[prop] = obj[prop]
      }
    }
    return cloneTarget
  }
  return target
}

深拷贝

递归

function deepClone(obj) {
    let cloneObj = {}
  for(let key in obj) {
      if(typeof obj[key] === 'object') {
        cloneObj[key] = deepClone(obj[key])
    } else {
        cloneObj[key] = obj[key]
    }
  }
  return cloneObj
}

虽然利用递归能实现以下深拷贝,但是同JSON.stringfy一样,还是有一些问题没有解决,例如:
这个深拷贝函数并不能复制不可枚举的属性以及Symbol类型

针对不能复制不可枚举类型以及Symbol类型,我们可以使用Reflect.ownKeys方法

这种方法只是针对普通的引用类型的值做递归复制,而对Array, Date, RegExp, Error, Function这样的引用类型并不能正确的拷贝

当参数为Date, RegExp类型,直接生成一个新的实例返回 利用Object的getOwnPropertyDescriptors方法可以获得对象的所有属性,以及对应的特性 顺便结合Object.create创建一个新对象,并传入原对象的原型链

对象的属性里面成环,即循环引用没有解决

利用WeakMap作为Hash表,因为WeakMap是弱引用类型,可以有效防止内存泄露,作为检测循环引用很有帮助,如果存在循环,则引用直接返回WeakMap存储的值

const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)