深拷贝,在前端面试里似乎是一个永恒的话题了,最简单的方法是JSON.stringify()以及JSON.parse(),但是这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,不可以拷贝 undefined , function, RegExp 等类型。还有其他一些包括扩展运算符、object.asign、递归拷贝、lodash 库等的实现,网上有很多相关资料和实现,这里不是我们讨论的重点。这次我们来探讨一个新的实现 —— MessageChannel。我们直接看代码:

    1. // 创建一个obj对象,这个对象中有 undefined 和 循环引用
    2. let obj = {
    3. a: 1,
    4. b: {
    5. c: 2,
    6. d: 3,
    7. },
    8. f: undefined,
    9. };
    10. obj.c = obj.b;
    11. obj.e = obj.a;
    12. obj.b.c = obj.c;
    13. obj.b.d = obj.b;
    14. obj.b.e = obj.b.c;
    15. // 深拷贝方法封装
    16. function deepCopy(obj) {
    17. return new Promise((resolve) => {
    18. const { port1, port2 } = new MessageChannel();
    19. port1.postMessage(obj);
    20. port2.onmessage = (e) => resolve(e.data);
    21. });
    22. }
    23. // 调用
    24. deepCopy(obj).then((copy) => {
    25. // 请记住`MessageChannel`是异步的这个前提!
    26. let copyObj = copy;
    27. console.log(copyObj, obj);
    28. console.log(copyObj == obj);
    29. });
    30. 复制代码

      我们发现MessageChannelpostMessage传递的数据也是深拷贝的,这和web workerpostMessage一样。而且还可以拷贝 undefined 和循环引用的对象。简单说,MessageChannel创建了一个通信的管道,这个管道有两个端口,每个端口都可以通过postMessage发送数据,而一个端口只要绑定了onmessage回调方法,就可以接收从另一个端口传过来的数据。

    需要说明的一点是:MessageChannel在拷贝有函数的对象时,还是会报错。