如何手动实现一个深拷贝?

这是一段深拷贝代码,为什么这里需要用到一个map,而且还是WeakMap。是为了解决什么问题?

function deepClone(obj, hash = new WeakMap()) {
  if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝
  if (typeof obj !== "object") return obj;
  // 循环引用属性的处理
  if (hash.get(obj)) return hash.get(obj);
  // 是对象的话就要进行深拷贝
  let cloneObj = new obj.constructor();
  // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
  hash.set(obj, cloneObj);
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // 实现一个递归拷贝
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }
  return cloneObj;
}
阅读 5.4k
5 个回答

使用 WeakMap 可以解决循环引用的问题

例如下例:

const target = {
    field1: 'filed1',
};
target.target = target;

const copy = deepClone(target)

因为递归会导致进入死循环导致栈内存溢出

为什么用WeakMap,不用Map,是为了垃圾回收,WeakMap是弱引用

关于WeakMap详情可参考
https://www.zhangxinxu.com/wo...

解决的是对象的循环引用,不用map的话会死循环

weakMap的key必须是对象,并且是弱引用

循环引用

var a = {name:"a"}
a.obj = a

如果没有 if (hash.get(obj)) return hash.get(obj);这段代码会无限递归下去。
WeakMap是为了不干扰垃圾回收机制:hash.set(obj, cloneObj);对obj产生的引用是弱引用。

有时候我们会把对象作为一个对象的键用来存放属性值,普通集合类型比如简单对象会阻止垃圾回收器对这些作为属性键存在的对象的回收,有造成内存泄漏的危险。而WeakMap则更加安全些,这些作为属性键的对象如果没有别的变量在引用它们,则会被回收释放掉。

代码中这部分不对

// 是对象的话就要进行深拷贝
  if (hash.get(obj)) return hash.get(obj);

应该是

// 自引用属性的处理
if (hash.get(obj)) return hash.get(obj);
// 是对象的话就要进行深拷贝

所以,为了解决自引用,所以需要一个额外的对象当做工作区,这样保证不会在复制过程中被自引用属性弄成死循环,然后产生的问题就是普通集合类型比如简单对象会阻止垃圾回收器对这些作为属性键存在的对象的回收,有造成内存泄漏的危险了,所以用WeakMap来避免。

用例子说是最清楚的
比如你要拷贝一个对象

let a={
    b:{
        c:a
    }
}

你去拷贝这个对象的时候,函数检查到 a 是对象,就去遍历
然后拿到 b,b 又是对象,就再去遍历,然后到 c,发现 c 也是一个对象,接着遍历,然后就死循环了。
所以我们需要做的是当遍历到 c 的时候,让他不要去遍历,而是直接返回 a 这个对象给 c。
于是我们需要一个数据结构记录下整条链路上有哪些已经遍历过的对象
纵观整个 js 的数据类型,你猜猜哪个数据类型可以做到这件事情呢?
对象可以吗?不行,对象的键值只能是 string,我们需要一个可以用对象作为键值的类型。
那就可以使用 Map(Set), 其实也可以。
那为什么用 WeakMap(WeakSet) 不用 Map(WeakSet) 呢?
这个你就需要去看看 WeakMap (WeakSet)相比于 Map(Set) 的优势在哪里。比如自动的垃圾回收这样,这个可以单独用一篇文章去回答的,所以建立另外单独看。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题