平时在对一个数据进行凑合用的 json化,我们会直接 JSON.stringify(obj) 输出一个一行的字符串,并且会自动过滤掉 undefined、Function、Symbol等内容,对于递出现循环引用则会直接报错。
但它实际上能够接3个参数,而且,JSON.stringify 并非触摸不到对象里面的 undefined、Function、Symbol等。
JSON.stringify(obj, replacer?, space?)
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
space 美化
space 大于0时是用来美化输出字符串,将内容输出成格式化后的效果,定义了缩进时的空格数。
const obj = {a: 1, b: [{ c: 2, d: 3 }]}
JSON.stringify(obj, null, 2);
/*
"{
"a": 1,
"b": [
{
"c": 2,
"d": 3
}
]
}"
*/
因此相比 js 代码需要用 ugilfyjs 等工具进行代码格式处理,json 格式的字符串可以直接用 JSON.stringify 完成排版美化。
replacer 遍历
replacer 是定义在 JSON.stringify 深度遍历时,处理到的键值对(如果是数组,键为下标;如果既不是对象也不是数组则没有键)进行一些特殊化处理,它支持数组和函数两种有效类型,其他类型都将当做不发生任何处理(且正常运行)。
- 数组:只输出指定 key 的内容(但其父节点必须存在)
const obj = {a: 1, b: [{ c: 2, d: 3 }], e: undefined, f: () => {}};
obj.toString(); // '[object object]'
JSON.stringify(obj); // '{"a":1,{"b":[{"c":2,"d":3}]}}'
JSON.stringify(obj, ['a', 'b']); // '{"a":1, "b":[{}]}'
JSON.stringify(obj, ['a', 'd']); // '{"a":1}',d 虽然指定了但它的节点b不在
JSON.stringify(obj, ['a', 'b', 'c']); // '{"a":1, "b":[{"c":2}]}'
因此,JSON.stirngify可以用在数据筛选、保留纯数据的地方,例如后台返回部分数据。
- 函数 (key, value) => value:自定义处理
如果想要筛选掉一些字段,而不是像上面保留指定字段,则需要用函数来实现:
JSON.stringify(obj, (key, value) => {
if (!['a', 'd'].includes(key)) {
return value;
}
}) // '{"b":{"c":2}}'
如果想保留输出字符串中含 undefined、Function 等内容,则可以不严谨地这样做:
JSON.stringify(obj, (key, value) => {
if (value === undefined) {
return '__undefined__';
} else if (value instanceof Function) {
return `__Function__${value.toString()}__Function__`;
}
return value;
})
.replace(/"__undefined__"/g, 'undefined')
.replace(/"?__Function__"?/g, '');
// {"a":1,"b":[{"c":2,"d":3}],"e":undefined,"fun":() => {}}
同时发现,JSON.stringify 以深度优先的递归算法遍历整个对象所有键值对:
因此我们可以利用它快速实现一个深度遍历对象的纯函数算法:
const DFTrace = (obj, handler) => {
JSON.stringify(obj, (key, value) => {
handler(key, value);
return value; // 这一句不可缺少
})
}