关于手写JSON.stringify

JSON.stringify是序列化对象的方法,可以将对象转换为字符串方便传输和储存,除此之外,用它来实现深拷贝也是棒棒的。为了对这个api有更深的理解,今天让我们用手写代码的方式来实现一下吧。

测试用例:

const wife = {
  name: 'name',
  age: 25,
  [Symbol()]: 222,
  birth:new Date(),
  nick:/wife/,
  like:undefined,
  cook:function(){console.log('cook')},
  husband:null,
  list:[{test:'ee'}],
  children: [
    {
      name: 'name',
      age: 26,
    },
    {
      name: 'name',
      age: 222,
    }
  ],
}

在手动实现json.stringify之前,先让我们来看一眼真正的JSON.stringify(wife)返回的是个什么样的玩意儿:

 {
  "name": "name",
  "age": 25,
  "birth": "2021-10-29T06:44:11.758Z",
  "nick": {},
  "husband": null,
  "list": [
    {
      "test": "ee"
    }
  ],
  "children": [
    {
      "name": "name",
      "age": 26
    },
    {
      "name": "name",
      "age": 222
    }
  ]
}

根据这个返回的结果,我们注意到了以下几个点

  1. value为日期对象的返回了一个字符串,但这个字符串好像也不是toString()直接返回的
  2. value为正则表达式的直接返回了一个空对象
  3. value为函数或者undefined或者Symbol的直接过滤掉了
  4. value为null的情况仍然返回为null
  5. value为数组仍然返回了一个数组,只是把数组中的每一项都序列化了

为什么会出现这种情况呢,这时我们打开了mdn(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)看到了以下说明

转换值如果有 toJSON() 方法,该方法定义什么值将被序列化。
Date 日期调用了 toJSON() 将其转换为了字符串(同Date.toString()),因此会被当做字符串处理。

有了这句话,第一点就清楚了,这个日期返回的字符串当然不是toString()的值,因为我们的日期对象有toJSON()方法,这个字符串是由toJSON()返回的

undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。函数、undefined 被单独转换时,会返回 undefined,如JSON.stringify(function(){}) or JSON.stringify(undefined).

有了这一点,第三点也迎刃而解了

NaN 和 Infinity 格式的数值及 null 都会被当做 null

嗯 解释了第四点

关于第二点和和第五点,mdn上没有明显的说明。但根据笔者理解,RegExp和Date一样作为一个特殊的对象但又没有toJSON方法,作为没有显式属性的对象转换出来自然成了一个{};而数组本来就是特殊的对象,输出{"0":ele1:"1":ele2}的话也太奇怪了。

弄清楚了JSON.stringify的序列化规则以后,让我们动手吧嘻嘻:

function myStringify(obj){
  //忽略undefind和function 先置为undefined 组合的时候忽略
  if(obj === undefined || typeof(obj) === 'function'){
    return undefined;
  }

  //null或者NaN Infinity置为null
  if(obj === null){
    return null;
  }

  // 时间对象转为字符串
  if(obj instanceof Date){
    return `"${obj.toJSON()}"`
  }

  //regExp输出{}
  if(obj instanceof RegExp){
    return `{}`
  }

  //字符串 置为“spring” 布尔 数字原样输出
  if(typeof(obj)!== 'object'){
    return typeof(obj) === 'string' ? `"${obj}"`:obj
  }

  //数组
  if(Array.isArray(obj)){
    let arrStr = obj.map(item => `${myStringify(item)}` )
   return `[${arrStr.join(',')}]`
  }

  //采用Object.getOwnPropertyNames直接过滤掉symbol
  let keyNames = Object.getOwnPropertyNames(obj);
  const arrObj = keyNames.map((item) => {
    return `${myStringify(obj[item]) !== undefined && `"${item}":${myStringify(obj[item])}`}`
  })
  return `{${arrObj.join(',')}}`
}

用这个方法序列化测试用例和JSON.stringify()一毛一样

问题:
如果这个方法要加入JSON.stringify的第二个参数,代码应该怎么样设计呢

你可能感兴趣的:(关于手写JSON.stringify)