JSON.stringify是将对象字符串化,一般用在前后端数据传递,用法也是非常的简单,例如:JSON.stringify({name:'aaa'})就得到了"{"name":"aaa"}",也就是常说的JSON字符串。但是如果这个对象里面存在循环引用的话,就会比较麻烦,如下:

我们可以发现此时JSON.stringify会报错。此时,可以用JSON-js去破除这种循环结构。

基本用法

JSON-js是老外写的一个对JSON处理的小工具,其中的decycle和retrocycle是专门用来破除/恢复这种循环结构的。

下面是包含decycle和retrocycle的核心实现的js代码:

先来看下decycle最简单的用法:

我们可以看到,破解循环后确实没有报错,但是出现了$ref:'$'这样的代码,这种标志表示识别除了循环引用,其中$ref为固定的,右边的'$...'表示它循环引用的部分,单个$为顶层对象,我们现在看个不是顶层对象的情况:

我们看到现在为$ref:$['link']['url'][0],我们画个示意图:

background Layer 1 a name link url url[0] b name link url url[0] href href 线路1 线路2 节点A 节点B 节点C 节点D

我们从对象a开始遍历,依次会经过节点A->节点B->线路1->节点C->节点D->线路2->节点A->.....,我们发现,经过线路2之后,路线就会发生重复,因此$ref:$['link']['url'][0]的意思就是表示在a['link']['url'][0]处发生了循环,因此不再遍历,只保留一个记号。

JSON.retrocycle则是恢复破解,识别key为$ref的,然后恢复引用。

代码思路分析

JSON.decycle是负责识别循环的,主要利用new WeakMap对象,将沿途遍历的对象进行存储,主要留意的是PlainObject和数组,其他的例如数字,字符串,Date对象,Boolean对象等不会导致循环问题。

JSON.retrocycle主要负责恢复循环,需要识别$ref后的内容,主要靠的是下面的正则:

这个正则看起来比较费力,但是主要识别三种情况:

1.\d+,识别[]中出现数字,例如[1],[123]等情况

2."[^\\"\u0000-\u001f]*",识别[]中没有\,",和\u0000-\u001f范围的任意字符,例如["link"],["def"]这种情况。\u0000-\u001f里面主要是例如:\t,\r,\n等控制符号。

3.\\(?:[\\"\/bfnrt]|u[0-9a-zA-Z]{4}),识别[]中出现的部分控制符号,以及转移字符,例如["\\b"],["\\t"],["\\u1234"]等。这种情况正常不会出现,但却是可以正常被解析的情况,例如:JSON.parse('{"name":"\\b"}'),JSON.parse('{"name":"\\u1234"}')都是合法的。

识别$ref的内容后,用eval进行恢复,这个恢复还是蛮有意思的:

eval中有一个$符号,然后给当前执行环境中设置$=a,那么$['link']就会变成a['link'],不过注意,如果是$['name']就会报错,因为$['name']的结果是'aaa',不是对象,eval执行不了。

其他文章

0
我要评论

评论

返回
×

我要评论

回复:

昵称:(昵称不超过20个字)

图片:

提交
还可以输入500个字