不同寻常的 JSON.stringify() 和 JOSN.parse()

1,050 阅读5分钟

在 JavaScript 中, JSON 是我们很常用的一个全局对象,它有两个方法:一个是 stringify() ,用来将 JavaScript对象序列化为JSON字符串;另一个是 parse(),用来将 JSON 字符串解析为原生 JavaScript 值。例如:

var userInfo = {
  name: "张三",
  age: 20,
  friends: [
    {
      name: "李四",
      age: 18,
    },
  ],
};
var jsonStr = JSON.stringify(userInfo);
var copyUserInfo = JSON.parse(jsonStr);
console.log(jsonStr);
console.log(copyUserInfo);

通过上面的例子,我们可以看出 JSON.stringify() 执行后返回了一个字符串,而 JSON.parse() 将这个字符串重新解析成了一个 JavaScript 对象,这个新的对象 copyUserInfo 的属性和我们最初的 userInfo 对象是完全相同的,但它们是两个独立的、没有任何关系的对象。利用这个特点,我们可以在一些场景中进行对象的深拷贝。

这是 JSON.stringify() 和 JOSN.parse() 最基础的用法,就不做过多介绍了,本文主要介绍一下,这两个方法的一些鲜为人知的秘密用法😜😜😜。


JSON.stringify(value[, replacer [, space]])

可能有些同学不知道, JSON.stringify() 方法,其实还有两个可选参数:replacer 和 space。下面我们来看一下,这两个参数到底是何方神圣。

  • replacer 是一个过滤器,它可以是一个数组,也可以是一个函数。
  • space 指定缩进用的空白字符串,用于美化输出,它可以是一个数字,也可以是一个字符串。

我们来通过下面的几个例子,来看一下这两个参数的具体用法。

var userInfo = {
  name: "张三",
  age: 20,
  friends: [
    {
      name: "李四",
      age: 18,
    },
  ],
};
var jsonStr = JSON.stringify(userInfo, ["name", "friends"]);
console.log(jsonStr); // '{"name":"张三","friends":[{"name":"李四"}]}'

我们将 replacer 传入了一个数组,这个数组包含 "name" 和 "friends",这两个属性与将要序列化的对象中的属性是对应的,在返回的结果字符串中,只包含了这两个属性。也就是说,只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中。

如果第二个参数是函数,行为会稍有不同。传入的函数接收两个参数,属性(键)名和属性值。根据函数中的逻辑计算每个键值对。如果返回 undefined 的键值对就不会输出。

var userInfo = {
  name: "张三",
  age: 20,
  friends: [
    {
      name: "李四",
      age: 18,
    },
  ],
};
var jsonStr = JSON.stringify(userInfo, function (key, value) {
  if (key === "age") {
    return value + 2;
  }
  if (Array.isArray(value)) {
    return undefined;
  }
  return value;
});
console.log(jsonStr); // '{"name":"张三","age":22}'

上面这两个例子的 JSON 序列化结果都是一个紧密的字符串,可读性不是很好,我们来看下 space 参数。

var userInfo = {
  name: "张三",
  age: 20,
  friends: [
    {
      name: "李四",
      age: 18,
    },
  ],
};
var jsonStr = JSON.stringify(userInfo, null, 2);
console.log(jsonStr);

我们将 space 传入了一个数字 2,通过输出结果我们可以看到字符串中自动插入了换行符和缩进空格,这样可读性是不是好很多了呢。这里需要注意的是,最大缩进空格数为10,所有大于10的值都会自动转换为10。

如果将space传入一个字符串而非数值,则这个字符串将在JSON字符串中被用作缩进字符(不再使用空格)。在使用字符串的情况下,可以将缩进字符设置为制表符,或者两个短划线之类的任意字符。

var userInfo = {
  name: "张三",
  age: 20,
  friends: [
    {
      name: "李四",
      age: 18,
    },
  ],
};
var jsonStr = JSON.stringify(userInfo, null, '--');
console.log(jsonStr)

注意,缩进字符串最长也是不能超过10个字符长的。如果字符串长度超过了10个,结果中将只出现前10个字符。

这里补充一下,在这两个例子中,replacer 传了一个 null ,这样对象所有的属性都会被序列化。

有时候,JSON.stringify() 还是不能满足对某些对象进行自定义序列化的需求。在这些情况下,可以通过 toJSON()方法来处理。如果一个被序列化的对象拥有 toJSON 方法,那么该 toJSON 方法就会覆盖该对象默认的序列化行为,不是该对象被序列化,而是调用 toJSON 方法后的返回值会被序列化,例如:

var userInfo = {
  name: "张三",
  age: 20,
  toJSON: function () {
    return {
      name: "李四",
      age: 12,
    };
  },
};
console.log(JSON.stringify(userInfo)); // '{"name":"李四","age":12}'

JSON.parse(text[, reviver])

和 JSON.stringify() 方法有一点不同,JSON.parse() 方法,只有一个可选参数:reviver。reviver 需要传入一个函数,这个函数也是接收两个参数,一个键一个值,而且都需要返回一个值。如果返回 undefine,则表示要从结果中删除相应的值,如果返回其他值,则将该值插入到结果中。

var userInfo = {
  name: "张三",
  age: 20,
  birthdate: "2001/03/12",
};
var jsonStr = JSON.stringify(userInfo);
console.log(jsonStr); // '{"name":"张三","age":20,"birthdate":"2001/03/12"}'
var copyUserInfo = JSON.parse(jsonStr, function (key, value) {
  if (key === "age") {
    return undefined;
  }
  if (key === "birthdate") {
    return new Date(value);
  }
  return value;
});
console.log(copyUserInfo.hasOwnProperty("name")); // true
console.log(copyUserInfo.hasOwnProperty("age")); // false
console.log(copyUserInfo.hasOwnProperty("birthdate")); // true
console.log(copyUserInfo.birthdate.getFullYear()); // 2001


小结

虽然我们平时在使用JSON.parse()和JSON.stringify()方法时,一般只需要使用第一个参数就可以解决问题,但是在一些场景中这几个可选属性可以更方便的解决一些问题,所以整理一下,希望能为大家带来便利。如果有不对的地方欢迎指正。动动可爱的小手,关注一下公众号哦,会不定期推送新文章哦😊😊😊