你不知道的 JSON.stringify

546 阅读3分钟

JSON.stringify 在日常开发中是经常遇到,但是你知道它其实有三个参数吗?

JSON.stringify(value, replacer, space)

我们一般只会用到第一个参数,即 value(被序列化的对象),后面两个参数是可选的。但是后面两个参数才是 JSON.stringify 有趣的地方。

replacer

第二个参数 replacer 可以让我们定制序列化的逻辑

默认情况下(该参数为 null 或 undefined),对象所有的属性都会被序列化,但是 replacer 还可以是函数或数组:

  • 如果 replacer 是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;
  • 如果 replacer 是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;

例如:

function replacer(key, value) {
  if (typeof value === "string") {
    return undefined
  }
  return value
}

var foo = {foundation: "Mozilla", model: "box", week: 45, transport: "car", month: 7}
var jsonString = JSON.stringify(foo, replacer) // {"week":45,"month":7}.

上面的 replacer 对需要序列化对象的字段起到了过滤作用,排除 string 类型的值,它是递归过滤的,比如把上面的 foo 对象修改一下:

var foo = {foundation: "Mozilla", model: {name: "box", week: 45, transport: "car"}, month: 7}
var jsonString = JSON.stringify(foo, replacer) // {"model":{"week":45},"month":7}

当 replacer 时函数的时候,它接收两个参数:键(key)和值(value),而它的返回值决定了该字段是否被序列化:

  • 返回 Number,转换成相应的字符串被添加入 JSON 字符串。
  • 返回 String,该字符串作为属性值被添加入 JSON。
  • 返回 Boolean,"true" 或者 "false"被作为属性值被添加入 JSON 字符串。
  • 返回任何其他对象,该对象递归地序列化成 JSON 字符串,对每个属性调用 replacer 方法。除非该对象是一个函数,这种情况将不会被序列化成 JSON 字符串。
  • 返回 undefined,该属性值不会在 JSON 字符串中输出。

其他都好理解,返回对象是什么意思呢?

function replacer(key, value) {
  if (key === "model") {
    return {hello: "world", model: 3}
  }
  return value
}
var foo = {foundation: "Mozilla", model: {name: "box", week: 45, transport: "car"}, month: 7}
var jsonString = JSON.stringify(foo, replacer) // {"foundation":"Mozilla","model":{"hello":"world"},"month":7}

原来就是用新对象替换了原来 key 的值,但是需要注意的是,在替换之前也会对该对象使用 replacer 方法进行过滤,加入上面的 replacer 函数写成下面的形式就会报错 Maximum call stack size exceeded,因为出现了无限递归:

function replacer(key, value) {
  if (key === "model") {
    return {hello: "world", model: 3}
  }
  return value
}

改成:

let count = 0
function replacer(key, value) {
  count++
  if (typeof value === "string" && count < 5) {
    return {name: "Davlid", count}
  }
  return value
}
var foo = {foundation: "Mozilla", model: {name: "box", week: 45, transport: "car"}, month: '7'}
var jsonString = JSON.stringify(foo, replacer)

上面的输出结果是:

{"foundation":{"name":{"name":{"name":"Davlid","count":4},"count":3},"count":2},"model":{"name":"box","week":45,"transport":"car"},"month":"7"}

replacer 除了可以是函数,还可以是数组:

var foo = {foundation: "Mozilla", model: "box", week: 45, transport: "car", month: 7}
JSON.stringify(foo, ['model', 'month']) // {"model":"box","month":7}

space

第三个参数 space 可以让我们定制序列化的显示效果

space 用来控制结果字符串里面的间距,取值可以是一个数字或字符串:

  • 如果是数字, 则在字符串化时每一级别会比上一级别缩进多这个数字值的空格(最多10个空格);
  • 如果是字符串,则每一级别会比上一级别多缩进用该字符串(或该字符串的前十个字符)。
var foo = {foundation: "Mozilla", model: "box", week: 45, transport: "car", month: 7}
JSON.stringify(foo, null, 2) 

会缩进2个空格,结果是:

{
  "foundation": "Mozilla",
  "model": "box",
  "week": 45,
  "transport": "car",
  "month": 7
}

如果用:

var foo = {foundation: "Mozilla", model: "box", week: 45, transport: "car", month: 7}
JSON.stringify(foo, null, '$') 

结果是:

{
$$"foundation": "Mozilla",
$$"model": "box",
$$"week": 45,
$$"transport": "car",
$$"month": 7
}