JavaScript 深拷贝的循环引用问题

如果说道实现深拷贝最简单的方法,我们第一个想到的就是 JSON.stringify() 方法,因为JSON.stringify()后返回的是字符串,所以我们会再使用JSON.parse()转换为对象,如下代码:

let obj = { name: 'liaoyi',age: 22,sex: 1} JSON.parse(JSON.stringify(obj)) 

但是这种克隆不够完美,有一个致命的问题无法解决,就是她一旦遇到循环引用就会报错:

let obj = { name: 'liaoyi',age: 22,sex: 1} JSON.parse(JSON.stringify(obj)) obj.c = obj console.log(JSON.stringify(obj)) 

js会报错,无法把一个循环引用转成 json 格式:

JavaScript 深拷贝的循环引用问题

在这种情况下,我们通常想到的是写一个正儿八经的深度克隆方法:

使用传统方式实现对象的深拷贝

function deepClone(obj) {   const objectMap = new Map();   const _deepClone = value => {     const type = typeof value;     if (type !== 'object' || type === null) {       return value;     }     if (objectMap.has(value)) {       return objectMap.get(value);     }     const result = Array.isArray(value) ? [] : {};      objectMap.set(value, result);      for (const [key, _v] of Object.entries(value)) {       result[key] = _deepClone(value[key]);       console.log(key, _v);     }     return result;   };   return _deepClone(obj); }  

使用 MessageChannel 实现循环引用对象的深拷贝

不够新鲜,我们来看一个好玩的 Web API

参考链接: MessageChannel

MessageChannel允许我们在不同的浏览上下文,比如window.open()打开的窗口或者iframe等之间建立通信管道,并通过两端的端口(port1和port2)发送消息。MessageChannel以DOM Event的形式发送消息,所以它属于异步的宏任务。

// 通过这个构造函数,创建一个消息通道,它会返回一个对象,解构 port1, port2 来实现通信 const { port1, port2 } = new MessageChannel(); port1.postMessage('hello') port2.onmessage = msg => {   console.log(msg.data)  // hello } 

我们可以利用这个API,实现循环引用对象的深拷贝:

 function deepClone(obj) {   return new Promise(resolve => {     const { port1, port2 } = new MessageChannel();     port1.postMessage(obj);      port2.onmessage = msg => {       resolve(msg.data);       // console.log(obj, msg.data === obj); // false     };   }) }   const obj = { a: 1, b: '2' }  obj.c = obj;   deepClone(obj).then(res =>{   console.log('res',res);  }) 

发表评论

相关文章