实现一个 JSON.stringify()

257 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

一、题目描述:

实现一个 JSON.stringify() 函数

let myObj = {
  undef: undefined,
  bool: false,
  fun: function(){},
  date: new Date(),
  arr: [1, 2],
  obj: {a: 1, b: 2},
  reg: /\d/,
  sym: Symbol(),
  nul: null,
  set: new Set(),
  map: new Map()
}
console.log(JSON.stringify(myObj))
// {"bool":false,"date":"2020-05-27T03:22:47.587Z","arr":[1,2],"obj":{"a":1,"b":2},"reg":{},"nul":null,"set":{},"map":{}}

这个例子使用 JSON.stringify() 把一个JavaScript 对象序列化为一个JSON 字符串,

这是上周我去Moka面试的时候出的一道面试题,当时还没写出来,他们公司有严格规定,出一道面试题如果写不出来就没办法进入下一步,直接挂掉。

二、思路分析:

现在,我们已经了解了 JSON.stringify() 方法的输出以及它的工作方式,让我们从序列化值开始实现它。

序列化值,首先,我们将从以下数据类型开始。

1、undefined

2、number

3、boolean

4、string


function stringify(value) {
  // 参数类型
  var type = typeof value;

  function getValues(value) {
    if (type === "undefined") {
      return undefined;
    }

    if (type === "number" || type === "boolean") {
      return "" + value + "";
    }

    if (type === "string") {
      return '"' + value + '"';
    }
  }

  return getValues(value);
}

console.log(stringify(1)); // "1"

console.log(stringify("abc")); // ""abc""

console.log(stringify(true)); // "true"

// 这里是 undefined 而不是 "undefined"
console.log(stringify(undefined) === JSON.stringify(undefined)); // true

到目前为止,上述功能是比较简单。它所做的只是用引号把值引起来,但是undefined并不需要转换为字符串,而是直接返回undefined数据类型。

现在,我们将添加对更多数据类型的支持,例如

1、array

2、object

3、null

4、date

5、functions (methods)

为了支持数组和对象,我们应该解析属性之间的多层嵌套。我们必须递归地处理子元素并序列化值。

对于数组而言,它非常简单,请使用一个开括号和一个闭括号对数组进行迭代,然后调用该stringify()函数,然后依次调用该getValues()函数并重复进行,直到所有值都考虑在内。

但是对于对象,我们需要使用对象字面量的左,右括号将值和属性的序列化。

对于日期对象,还有一件有趣的事情,JSON.stringify()方法返回的值为 ISO 8601 日期字符串(与在Date对象上调用toISOString()的结果完全一样)。

function stringify(value) {
  var type = typeof value;

  function getValues(value) {
    if (type === "undefined" || type === "function") {
      return undefined;
    }

    if (type === "number" || type === "boolean") {
      return "" + value + "";
    }

    if (type === "string") {
      return '"' + value + '"';
    }
  }

  // 对于对象数据类型
  // 在javascript中,数组和对象都是对象
  if (type === "object") {
    // 检查值是否为null
    if (!value) {
      return "" + value + "";
    }

    // 检查值是否为日期对象
    if (value instanceof Date) {
      return '"' + new Date(value).toISOString() + '"'; // 返回ISO 8601日期字符串
    }

    // 检查值是否为Array
    if (value instanceof Array) {
      return "[" + value.map(stringify) + "]"; // 递归调用stringify函数

    } else {
      // 否则它只是一个对象
      // 递归调用stringify函数
      return (
        "{" +
        Object.keys(value).map(
          key => '"' + key + '"' + ":" + stringify(value[key])
        ) +
        "}"
      );
    }
  }

  return getValues(value);
}

console.log(stringify([1, 2, 3])); // "[1,2,3]"

console.log(stringify(new Date())); // prints date in ISO format

console.log(stringify({ a: 1 })); // ""{a:1}""

console.log(stringify(myObj) === JSON.stringify(myObj)); // true

console.log(JSON.parse(stringify(myObj))); // 类似 JSON.parse(JSON.stringify(myObj))

四、总结:

实际上,JSON.stringify() 除了要序列化的JavaScript 对象外,还可以接收另外两个参数,这两个参数用于指定以不同的方式序列化JavaScript 对象。第一个参数是个过滤器,可以是一个数组,也可以是一个函数;第二个参数是一个选项,表示是否在JSON 字符串中保留缩进。单独或组合使用这两个参数,可以更全面深入地控制JSON 的序列化。(这里实现的代码暂时不支持者两个参数)

与JavaScript 不同,JSON 中对象的属性名任何时候都必须加双引号。手工编写JSON 时,忘了给对象属性名加双引号或者把双引号写成单引号都是常见的错误。