写点什么

你不知道的 JSON.stringify(上)

发布于: 2020 年 05 月 22 日
你不知道的JSON.stringify(上)

最近使用 Vue 写后台管理系统,在做 Tab 组件的持久化时遇到一个问题:

看意思应该是产生了循环引用的结构,下面是不同浏览器对于这个类型错误报出错的形式:

TypeError: cyclic object value (Firefox)
TypeError: Converting circular structure to JSON (Chrome and Opera)
TypeError: Circular reference in value argument not supported (Edge)

举个例子🌰:

var circularReference = {otherData: 123};circularReference.myself = circularReference;

此时到控制台里面运行一下:

JSON.stringify(circularReference);
// 报错信息如下
VM685:1 Uncaught TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
--- property 'myself' closes the circle
at JSON.stringify ()
at:1:6

可以看到和我上面说的 Vue 的例子是类似的。

那如何解决呢?

既然是由于循环引用导致的,那我们可以在发生循环引用的地方给切断。

那如何切断呢?

幸好 JSON.stringify 方法提供了便利,语法如下:

JSON.stringify(value[, replacer[, space]])

replacer 可以作为一个函数传入,且接受 key 和 value 作为入参,如下:

JSON.stringiify({},function(key,value){
// do sth
})

那解决方案就有啦:

JSON.stringify(circularReference,function(key,value){
if(key == 'myself'){ // 这里的key的判断条件,就是上面报错信息里的 property 'xxx' closes the circle,这里xxx是什么条件就是什么
return
}
return value
})

每次需要定位 key 值,如果嫌麻烦还有更简便的方法:

const getCircularReplacer = () => {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
}

推荐几个解决类似问题的库:

  • circular-json (只维护,vue-devtools 内部也使用它)

  • flatted (上面的继承者)

  • json-stringify-safe

  • cycle.js



发布于: 2020 年 05 月 22 日阅读数: 1312
用户头像

专注前端,但不限前端。 2017.10.17 加入

前端开发工程师

评论 (3 条评论)

发布
用户头像
getCircularReplacer 方法会误杀非循环的相同 obj,如 {a:obj, b:obj}
2020 年 05 月 22 日 17:52
回复
用户头像
关键是怎么能写出这种数据结构
2020 年 05 月 22 日 17:24
回复
用户头像
感谢分享干货,InfoQ首页推荐啦。
2020 年 05 月 22 日 11:12
回复
没有更多了
你不知道的JSON.stringify(上)