解决JS对象序列化&反序列化中循环引用和特殊值等问题

151 阅读2分钟

现象:

  1. 最常见的实现[object,object]对象序列化和反序列化的代码是JSON.stringify,但是JSON.stringify的缺点是:

    1. 会忽略对象中的函数和undefined值以及Symbol的值,在序列化后会自动删除这部分的值
    2. const sym = Symbol('example')
      const obj = {
          name:'John',
          getName:function(){
              return this.name;
          },
          [sym]:'value',
          undeStr:undefined
      }
      
      console.log(JSON.stringify(obj))//{"name":"John"}
      
    3. 针对循环引用的代码会显示错误
    4. const obj1 = {}
      const obj2 = {
          ref:obj1
      }
      obj1.ref = obj2
      
      try{
          const a = JSON.stringify(obj1)
      }catch(error){
          console.log('error'+error.message)
      }
      
    5. 针对某些特殊的object对象会没有办法解析,会改变其值变成string对象
    6. const obj = {
          ref0:new Date()
      }
      console.log(JSON.stringify(obj))//{"ref0":"2025-05-09T01:23:02.236Z"}
      

解决办法:

  1. 针对以上的JSON.stringify的解决办法,可以引入SuperJSON这个库,去进一步封装以用于适应自己的项目,SuperJSON的好处:

    1. 可以很好处理上面的JSON.stringify和JSON.parse的问题
    2. 对于TS友好,针对于数据类型不会将所有未知/自定义的数据类型转成any
    3. 支持懒加载对于大型的json数据,可以实现懒加载配置

SuperJSON的工作:

  1. 为部分常见的特殊类型添加类型标记,以便在反序列化的时候恢复原始类型,比如Date类型
  2. 针对部分的自己预定义的类型,根据原型链注册转换器,isApplicable:判断值是否属于目标类型;serialize:将值转换为 JSON 兼容格式;deserialize:从 JSON 数据恢复原始类型。
//针对常见的特殊类型会自动建立标记
{
    "superjson":{
        "type":"Date",
        "value":"2025-05-09T00:00:00.000Z"
    }
}

class Point {
  constructor(public x: number, public y: number) {}
}
//项目注册相关自定义类型,即可使用
SuperJSON.registerCustom(
  {
    isApplicable: (value): value is Point => value instanceof Point,
    serialize: (point) => ({ x: point.x, y: point.y }),
    deserialize: (data) => new Point(data.x, data.y)
  },
  'Point' // 类型标识符
);

实际应用案例:

  1. localstorage和sessionstorage一般都只能接受json字符串的存储格式。
  2. 后端传的数据可能为了保持实时性,传的是Date或者正则等格式的数据
  3. 可能在某些场景上,数据是存在循环应用的case