9

JavaScript 深拷贝的循环引用问题 - 是廖一啊

 1 year ago
source link: https://www.cnblogs.com/liao-yi/p/17007875.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

JavaScript 深拷贝的循环引用问题

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

highlighter- code-theme-dark TypeScript
let obj = { name: 'liaoyi',age: 22,sex: 1}
JSON.parse(JSON.stringify(obj))

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

highlighter- code-theme-dark TypeScript
let obj = { name: 'liaoyi',age: 22,sex: 1}
JSON.parse(JSON.stringify(obj))
obj.c = obj
console.log(JSON.stringify(obj))

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

2024808-20221227123253005-1761349014.png

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

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

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的形式发送消息,所以它属于异步的宏任务。

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

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

highlighter- code-theme-dark TypeScript
 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);
 })

__EOF__

本文作者: lioayi 本文链接: https://www.cnblogs.com/liao-yi/p/17007875.html 关于博主: 评论和私信会在第一时间回复。或者直接私信我。 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处! 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK