深拷贝,在前端面试里似乎是一个永恒的话题了,最简单的方法是JSON.stringify()以及JSON.parse(),但是这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,不可以拷贝 undefined , function, RegExp 等类型。还有其他一些包括扩展运算符、object.asign、递归拷贝、lodash 库等的实现,网上有很多相关资料和实现,这里不是我们讨论的重点。这次我们来探讨一个新的实现 —— MessageChannel。我们直接看代码:
// 创建一个obj对象,这个对象中有 undefined 和 循环引用let obj = {a: 1,b: {c: 2,d: 3,},f: undefined,};obj.c = obj.b;obj.e = obj.a;obj.b.c = obj.c;obj.b.d = obj.b;obj.b.e = obj.b.c;// 深拷贝方法封装function deepCopy(obj) {return new Promise((resolve) => {const { port1, port2 } = new MessageChannel();port1.postMessage(obj);port2.onmessage = (e) => resolve(e.data);});}// 调用deepCopy(obj).then((copy) => {// 请记住`MessageChannel`是异步的这个前提!let copyObj = copy;console.log(copyObj, obj);console.log(copyObj == obj);});复制代码
我们发现MessageChannel的postMessage传递的数据也是深拷贝的,这和web worker的postMessage一样。而且还可以拷贝 undefined 和循环引用的对象。简单说,MessageChannel创建了一个通信的管道,这个管道有两个端口,每个端口都可以通过postMessage发送数据,而一个端口只要绑定了onmessage回调方法,就可以接收从另一个端口传过来的数据。
需要说明的一点是:
MessageChannel在拷贝有函数的对象时,还是会报错。
