调接口的时候,console.log 打出来对象里明明有 token 字段,JSON.stringify 之后变成了空对象,两个花括号里面什么都没有。

stringify 只序列化可枚举的自有属性,而且对值有要求。几种情况会被直接跳过。

会被丢掉的值

  • undefined、函数、Symbol:作为对象属性时整个键被省略,作为数组元素时变成 null。
  • NaN 和 Infinity:变成 null。
  • Date:调用自身的 toJSON,变成 ISO 格式字符串,原来的时区信息没了。
  • Map 和 Set:变成空对象,因为元素不算自有属性。
  • BigInt:直接抛 TypeError。

从类实例上取属性也会丢。原型链上的 getter 不算自有属性,序列化出来是空的,得先转成普通对象。

循环引用会报错

对象引用自己,比如父节点里存了子节点,子节点又指回父节点,stringify 时抛 Converting circular structure to JSON。把组件树或者框架的响应式对象直接序列化,最容易撞上。

处理办法是第二个参数传替换函数,遇到重复对象返回 undefined;或者先用结构化拷贝剥一层。不要用先序列化再反序列化的方式做深拷贝,函数和日期都会坏掉。

三个参数怎么用

第二个参数传数组,只保留列出的键,做接口脱敏很方便。传函数,每个键值对过一遍,去掉密码字段、把金额转成字符串,都在这做。第三个参数是缩进,调试时给 2。

另外,stringify 还能认对象上的 toJSON 方法,返回什么就序列化成什么,日期就是这么处理的。

几个细节

undefined 作为顶层值,stringify 返回 undefined 而不是字符串,写缓存时要注意。JSON 不支持注释和尾逗号,解析时多一点东西就报错。

顺序也有讲究:整数键会按升序排到前面,字符串键保持插入顺序。依赖键顺序做签名的接口,得留神。

数据量大的时候,stringify 是同步执行的,几十兆的对象会把页面卡住。分批发或者交给后端拼,比硬撑着强。

遇到字段消失,先对着上面那张清单过一遍,八成能对上。