循环引用对象使用JSON.stringify方法报错

这里用value过滤循环引用属性没有报错

"use strict";
let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  occupiedBy: [{ name: "John" }, { name: "Alice" }],
  place: room
};

// 循环引用
room.occupiedBy = meetup;
meetup.self = meetup;

alert(JSON.stringify(meetup, function replacer(key, value) {

  return (key != "" && value == meetup) ? undefined : value;  //这个不报错
 // return (key == 'occupiedBy') ? undefined : value;       // 这个会报错!

}));

但是用key来过滤就会报错

"use strict";
let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  occupiedBy: [{ name: "John" }, { name: "Alice" }],
  place: room
};

// 循环引用
room.occupiedBy = meetup;
meetup.self = meetup;

alert(JSON.stringify(meetup, function replacer(key, value) {

  // return (key != "" && value == meetup) ? undefined : value;  //这个不报错
  return (key == 'occupiedBy') ? undefined : value;       // 这个会报错!

}));

这是什么原因呢

报错放出来看看

【以下回答由 GPT 生成】

这个问题是由于循环引用导致的。循环引用是指对象中存在互相引用的情况,例如一个对象A引用了另一个对象B,而对象B又引用了对象A。当我们使用JSON.stringify时,会尝试将对象转换为JSON格式的字符串,但是由于循环引用的存在,会导致无限递归的问题,从而报错。

在你的示例代码中,room.occupiedBy = meetup引起了循环引用。当你使用JSON.stringify时,它尝试将meetup对象转换为字符串。由于循环引用的存在,当它尝试序列化room对象时,会再次遇到meetup对象,导致无限递归。为了解决这个问题,JSON.stringify方法提供了一个可选的replacer参数,在这个参数中可以根据需要过滤掉某些属性。

在你的问题中,你尝试使用replacer函数来过滤属性,但是使用key来过滤属性却报错。这是因为在循环引用的情况下,当你尝试过滤meetup对象中的属性时,由于meetup对象的引用已经改变,meetup对象在处理到occupiedBy属性时,key所指向的属性实际上已经被重定义为undefined,而不是"occupiedBy"。这导致了报错。

要解决这个问题,一种可能的方法是在replacer函数内部维护一个变量,用来存储已经处理过的对象,避免无限递归。下面是一个可能的解决方案:

"use strict";
let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  occupiedBy: [{ name: "John" }, { name: "Alice" }],
  place: room
};

// 循环引用
room.occupiedBy = meetup;
meetup.self = meetup;

function replacer(key, value) {
  // 检查循环引用
  if (typeof value === 'object' && value !== null) {
    if (visitedObjects.has(value)) {
      return; // 如果已处理过, 返回undefined
    }
    visitedObjects.add(value);
  }
  return value;
}

let visitedObjects = new WeakSet();
console.log(JSON.stringify(meetup, replacer));

在上面的代码中,我们使用了一个visitedObjects集合来存储已经处理过的对象。当我们遇到一个对象时,首先检查它是否已经在visitedObjects集合中,如果是,说明已经处理过这个对象,直接返回undefined。如果不是,将这个对象添加到visitedObjects集合中,并返回原始的属性值。

这样,我们就可以避免循环引用导致的无限递归问题,并成功地将循环引用的对象转换为JSON格式的字符串。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^