JSON 方法,toJSON

114 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

假设我们有一个复杂的对象,我们希望将其转换为字符串,以通过网络发送,或者只是为了在日志中输出它。

当然,这样的字符串应该包含所有重要的属性。

我们可以像这样实现转换:

let user = {
  name: "John",
  age: 30,

toString() {
    return `{name: "${this.name}", age: ${this.age}}`;
  }
};

alert(user); // {name: "John", age: 30}

……但在开发过程中,会新增一些属性,旧的属性会被重命名和删除。每次更新这种 toString 都会非常痛苦。我们可以尝试遍历其中的属性,但是如果对象很复杂,并且在属性中嵌套了对象呢?我们也需要对它们进行转换。

幸运的是,不需要编写代码来处理所有这些问题。这项任务已经解决了。

JSON.stringify

JSON(JavaScript Object Notation)是表示值和对象的通用格式。在 RFC 4627 标准中有对其的描述。最初它是为 JavaScript 而创建的,但许多其他编程语言也有用于处理它的库。因此,当客户端使用 JavaScript 而服务器端是使用 Ruby/PHP/Java 等语言编写的时,使用 JSON 可以很容易地进行数据交换。

JavaScript 提供了如下方法:

  • JSON.stringify 将对象转换为 JSON。
  • JSON.parse 将 JSON 转换回对象。

例如,在这里我们 JSON.stringify 一个 student 对象:

let student = {
  name: 'John',
  age: 30,
  isAdmin: false,
  courses: ['html', 'css', 'js'],
  spouse: null
};

let json = JSON.stringify(student);

alert(typeof json); // we've got a string!

alert(json);
/* JSON 编码的对象:
{
  "name": "John",
  "age": 30,
  "isAdmin": false,
  "courses": ["html", "css", "js"],
  "spouse": null
}
*/ 

方法 JSON.stringify(student) 接收对象并将其转换为字符串。

得到的 json 字符串是一个被称为 JSON 编码(JSON-encoded)  或 序列化(serialized)  或 字符串化(stringified)  或 编组化(marshalled)  的对象。我们现在已经准备好通过有线发送它或将其放入普通数据存储。

请注意,JSON 编码的对象与对象字面量有几个重要的区别:

  • 字符串使用双引号。JSON 中没有单引号或反引号。所以 'John' 被转换为 "John"
  • 对象属性名称也是双引号的。这是强制性的。所以 age:30 被转换成 "age":30

JSON.stringify 也可以应用于原始(primitive)数据类型。

JSON 支持以下数据类型:

  • Objects { ... }

  • Arrays [ ... ]

  • Primitives:

    • strings,
    • numbers,
    • boolean values true/false
    • null

例如:

// 数字在 JSON 还是数字
alert( JSON.stringify(1) ) // 1

// 字符串在 JSON 中还是字符串,只是被双引号扩起来
alert( JSON.stringify('test') ) // "test"

alert( JSON.stringify(true) ); // true

alert( JSON.stringify([1, 2, 3]) ); // [1,2,3]

JSON 是语言无关的纯数据规范,因此一些特定于 JavaScript 的对象属性会被 JSON.stringify 跳过。

即:

  • 函数属性(方法)。
  • Symbol 类型的键和值。
  • 存储 undefined 的属性。

let user = {
  sayHi() { // 被忽略
    alert("Hello");
  },
  [Symbol("id")]: 123, // 被忽略
  something: undefined // 被忽略
};

alert( JSON.stringify(user) ); // {}(空对象)

通常这很好。如果这不是我们想要的方式,那么我们很快就会看到如何自定义转换方式。

最棒的是支持嵌套对象转换,并且可以自动对其进行转换。

例如:

let meetup = {
  title: "Conference",
room: {
    number: 23,
    participants: ["john", "ann"]
  }
};

alert( JSON.stringify(meetup) );
/* 整个结构都被字符串化了
{
  "title":"Conference",
  "room":{"number":23,"participants":["john","ann"]},
}
*/

重要的限制:不得有循环引用。

例如:

let room = {
  number: 23
};

let meetup = {
  title: "Conference",
  participants: ["john", "ann"]
};

meetup.place = room;       // meetup 引用了 room
room.occupiedBy = meetup; // room 引用了 meetup

JSON.stringify(meetup); // Error: Converting circular structure to JSON

在这里,转换失败了,因为循环引用:room.occupiedBy 引用了 meetupmeetup.place 引用了 room