Js中的JSON序列化的bug

299 阅读3分钟

1. JSON 序列化引发的血案

需求: 我想在localStorage存储一个用户信息, 便于后续使用。

实现:

 window.localStorage.setItem('key', { name: 'zs', age: 18 })

结果: [object Object]

image.png

解决: 使用JSON序列化转为字符串存储

window.localStorage.setItem('key', JSON.stringify({ name: 'zs', age: 18 }))

迭代: 我想在存储的用户信息中, 添加一个存储时间。

满足:

 window.localStorage.setItem('key', JSON.stringify({ name: 'zs', age: 18, date: new Date() }))

使用: 我想把用户的时间取出来调用其方法获取日期

    let user = JSON.parse(window.localStorage.getItem('key'))
    let date = user.date.getMonth()
    // Uncaught TypeError: date.getMonth is not a function
    console.log(user.date) // 这个时候已经变成了一个字符串

其他参数呢?:

  • 转换值如果有 toJSON() 方法,该方法定义什么值将被序列化。
  • 非数组对象的属性不能保证以特定的顺序出现在序列化后的字符串中。
  • 布尔值、数字、字符串的包装对象在序列化过程中会自动转换成对应的原始值。
  • undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined,如JSON.stringify(function(){}) or JSON.stringify(undefined).
  • 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
  • 所有以 symbol 为属性键的属性都会被完全忽略掉,即便 replacer 参数中强制指定包含了它们。
  • Date 日期调用了 toJSON() 将其转换为了 string 字符串(同 Date.toISOString()),因此会被当做字符串处理。
  • NaN 和 Infinity 格式的数值及 null 都会被当做 null。
  • 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性。

image.png

JSON.stringify() 是什么?

JSON.stringify 方法是将一个 JavaScript 对象或值转换为 JSON 字符串,默认该方法其实有三个参数:第一个参数是必选,后面两个是可选参数非必选。

第一个参数传入的是要转换的对象;

第二个是一个 replacer 函数,比如指定的 replacer 是数组,则可选择性地仅处理包含数组指定的属性;

第三个参数用来控制结果字符串里面的间距

那么我们可以利用第二个参数, 对特殊的值进行处理了。

  const data = {
      a: "aaa",
      b: undefined,
      c: Symbol("dd"),
      fn: function() {
        return true;
      }
    };
    // 不用 replacer 参数时
    JSON.stringify(data); 

// "{"a":"aaa"}"
// 使用 replacer 参数作为函数时
JSON.stringify(data, (key, value) => {
  switch (true) {
    case typeof value === "undefined":
      return "undefined";
    case typeof value === "symbol":
      return value.toString();
    case typeof value === "function":
      return value.toString();
    default:
      break;
  }
  return value;
})
// "{"a":"aaa","b":"undefined","c":"Symbol(dd)","fn":"function() {\n    return true;\n  }"}"

Tips: 这也是我们有时候对后端数据,进行JSON.parse(JSON.stringify())深拷贝时,出现的数据丢失问题。

2. JS 深拷贝的三种方法《推荐第三种》

  1. 使用递归
    function deepClone(obj){
    let objClone = Array.isArray(obj)?[]:{};
    if(obj && typeof obj==="object"){
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                //判断obj子元素是否为对象,如果是,递归复制
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone(obj[key]);
                }else{
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}  
  1. JSON.parse(JSON.stringify) 慎用
    JSON.parse(JSON.stringify(obj))
  1. 函数库lodash中的_.cloneDeep方法
    var _ = require('lodash')
    let obj = { name: 'xm', age: 12 }
    let newObj = _.cloneDeep(obj`)
    // obj === newObj false