Jansiel Notes

你不知道的JSON.parse()和JSON.stringfy()

前言

相信大多数人使用JSON.parse()和JSON.stringfy()这两个方法,只知道它们的第一个参数,这也是大多数人用的最多的功能,将JSON数据转化为JS对象。但是实际上,这两个方法除了第一个参数之外,还有其它参数,并且在转换过程中发挥着重要的作用。

方法详解

JSON.parse()

参数说明

JSON.parse()一共有两个参数,具体使用语法和参数的值、作用如下图所示

1JSON.parse(text[, reviver])
2// text,必传,要被解析的JSON字符串
3// reviver,选传,转换时的参数
4

reviver函数,可以对需要转换的数据进行遍历,并按照要求对数据进行处理,函数如果有返回值,则key值对应的value值就是返回值;如果没有返回值(或返回undefined),则该属性会被删除。

使用场景一

当需要将JSON数据转化为JS对象,并对数据进行处理时,平时我们可能是先将数据进行转换,再进行其它处理,实际上在转换的过程中可以传入第二个参数对数据进行处理。
例如现在我们一个JSON数据的数组,我们需要将其转化为JS数据,并对其中某个属性进行操作,可以用以下的方法:

 1let array = '[{"name": "apple", "price": 100}, {"name": "peach", "price": 300}, {"name": "watermelon", "price": 100}]';
 2array = JSON.parse(array, function (key, value) {
 3  console.log('key', key);
 4  console.log('value', value);
 5  /*
 6   key name, value apple, key price, value 100, key 0, value {name: 'apple', price: 400}
 7   key name, value peach, key price, value 300, key 1, value {name: 'peach', price: 600}
 8   key name, value watermelon, key price, value 100, key 2, value {name: 'watermelon', price: 400}
 9   key   , value [{name: 'apple', salary: 400}, {name: 'peach', salary: 600}, {name: 'waterwelon', salary: 400}]
10  */
11     // 对价格进行处理
12     if (key === 'price') {
13       return value + 300
14     }
15     // 不处理的返回原来的值
16     return value
17 })
18 console.log(array) // [{name: 'apple', salary: 400}, {name: 'peach', salary: 600}, {name: 'waterwelon', salary: 400}]
19

从上述代码我们可以看出,在使用JSON.parse()的时候,可以传入第二个参数,并且参数为一个函数,函数的参数分别为 keyvalue。函数执行的过程,首先是对JSON数组进行遍历,如果值是对象,会依次对对象的属性进行遍历,其中 key 的值为属性名, value 的值为属性名,而函数 return 的值会作为该 key 的新的属性值,基于这种方式我们就可以在转化过程中根据不同的 key 值做不同的处理。当对象遍历完,下一次遍历得到的则是数组的下标和整个对象的值,最后一次遍历则是返回转化完的整个值。

使用场景二

这里需要注意的是,在处理函数中,返回值决定了 key 值对应的值,如果返回的是undefined,则会删除该属性,基于该特性也可以在转化过程中进行属性的删除。

 1let array = '[{"name": "apple", "price": 100}, {"name": "peach", "price": 300}, {"name": "watermelon", "price": 100}]';
 2array = JSON.parse(array, function (key, value) {
 3     // 返回 undefined 删除价格属性
 4     if (key === 'price') {
 5       return undefined
 6     }
 7     // 不处理的返回原来的值
 8     return value
 9 })
10 console.log(array) // [{name: 'apple'}, {name: 'peach'}, {name: 'waterwelon'}]
11

实现思路

这里简单讲一下JSON.parse()的实现思路,不考虑其它复杂的场景,代码如下:

 1function myJsonParse(text, reviver) {
 2  // 通过eval函数可以把字符串转成对象
 3  // text = eval("(" + text + ")");
 4  // 文档建议不使用 eval 进行转换,而是使用 Function
 5  text = Function('"use strict";return (' + text + ")")();
 6  // 内部函数
 7  function objParse(holder, key) {
 8    let k;
 9    let v;
10    let value = holder[key];
11    // 判断值是不是对象
12    if (value && typeof value === "object") {
13      // 对象遍历
14      for (k in value) {
15        if (Object.prototype.hasOwnProperty.call(value, k)) {
16          // 递归调用,接受执行reviver函数后的值
17          v = objParse(value, k);
18          // 如果不是undefined,则把最新的值赋值给k
19          if (v !== undefined) {
20            value[k] = v;
21          } else {
22            // 如果是undefined,则删除该属性,这也就为什么可能过滤属性的根本原因了
23            delete value[k];
24          }
25        }
26      }
27    }
28    // 返回执行reviver函数后的值
29    return reviver.call(holder, key, value);
30  }
31
32  // 验证是否有传reviver且reviver是否是函数
33  return reviver && typeof reviver === "function"
34    ? objParse({ "": text }, "")
35    : text;
36}
37

小结

JSON.parse()的第二个参数,不仅可以处理JSON格式的数组,对象也是可以进行处理的,处理逻辑同处理数组时一致。对于eval()和Function()的使用,大家可以查看。

JSON.stringfy()

参数说明

JSON.stringfy()一共有三个参数,具体使用语法和参数的值、作用如下图所示

1JSON.stringify(value[, replacer [, space]])
2// obj,必传,要转换成JSON字符串的对象
3// replacer,选传,转换时的参数
4// space,选传,指定缩进用的空白字符串,用于美化输出
5

这里的 replacer 可以为数组,也可以为函数,当它为函数时使用方式与JSON.parse()一致,在这里就不过多讲解了,下面就简单讲下数组的用法。

1let array = [{name: "apple", price: 100}, {name: "peach", price: 300}, {name: "watermelon", price: 100}];
2array = JSON.stringfy(array, ['name'])
3console.log(array) // "[{"name": 'apple'}, {"name": 'peach'}, {"name": 'waterwelon'}]"
4

如果第二个参数为数组,则可以指定转化后的数据只保留数组中包含的 key 所对应的值。

实现思路

其实当使用JSON.stringfy()的时候,是调用了对象本身的toJSON(),如果对对象的toJSON()进行重写,当调用JSON.stringfy()时则返回的是重写后函数的返回值

1let obj = { a: 1 }
2obj.toJSON = function() {
3  return 'toJSON'
4}
5JSON.stringfy(obj) // 'toJSON'
6

注意事项

在使用JSON.stringfy()的过程中,还存在着许多需要注意的点

 1/*
 2    1、循环引用时或尝试去转换BigInt类型的值会抛出TypeError
 3    2、对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
 4    3、序列化对象的值有undefined、任意的函数以及symbol值,分为三种情况:
 5      3-1、在数组中,在序列化过程中会被转换成null
 6        function test(){}
 7        // [null,null,null]
 8        console.log(JSON.stringify([undefined, test, Symbol("")]))
 9      3-2、在非数组对象的属性值中,在序列化过程中会被忽略、
10        // 有些对象属性的确可能会是这些类型,在序列化时会被忽略
11        let object = {
12          name: 'zs',
13          age: 18,
14          sex: undefined,
15          say: function () {
16
17          },
18          id: Symbol()
19        }
20        // {"name":"zs","age":18}
21        console.log(JSON.stringify(object))
22      3-3、单独时,都会返回undefined
23        // undefined
24        console.log(JSON.stringify(undefined))
25        // undefined
26        console.log(JSON.stringify(function () {}))
27        // undefined
28        console.log(JSON.stringify(Symbol('1213')))
29      3-4、NaN 和 Infinity 格式的数值及 null 都会被当做 null
30        let object = {
31          name: "zs",
32          age: NaN,
33          money: Infinity,
34        };
35        // {"name":"zs","age":null,"money":null}
36        console.log(JSON.stringify(object));
37        // [null,null]
38        console.log(JSON.stringify([NaN, Infinity]))
39*/
40

更多使用时的注意事项大家可以查看

总结

可能大家在使用这些API的时候,只使用了部分功能,其它功能没有涉及到所以基本上不了解,我也是在某次偶然的机会中查看到了文档,才知道这些方法其实还有更多的用途而并不局限于对数据类型的转换。希望大家看完这篇文章能有所收获,在今后的学习工作中,对于使用的API,最好还是参考着文档使用,说不定还隐藏着某些你没用过但是又十分好用的功能。