JavaScript 中的 `JSON` 类
本文介绍了 JavaScript 中的 JSON 类。
我们将通过实例讲解 JavaScript 中的 JSON 类。
YouTube Video
JavaScript 中的 JSON 类
JSON 对象主要有两个方法。JSON.stringify() 用于将对象转换为 JSON 字符串,JSON.parse() 用于从 JSON 字符串创建对象。JSON 是一种只能表达部分 JavaScript 值的数据交换格式。函数、undefined、Symbol 和循环引用无法直接转换为 JSON。
JSON.stringify()
首先,下面是一个将对象转换为 JSON 字符串的基本示例。
1// Convert a JavaScript object to a JSON string.
2const obj = { name: "Alice", age: 30, active: true };
3const jsonString = JSON.stringify(obj);
4console.log(jsonString); // {"name":"Alice","age":30,"active":true}
- 这段代码是将普通对象转换为 JSON 字符串的最基本示例。
JSON.stringify会同步返回字符串。
JSON.stringify 的第二个参数(replacer)
通过指定 JSON.stringify 的第二个参数 replacer,可以精细控制转换规则。以下是用数组指定只保留特定属性的示例。
1// Use a replacer array to include only specific properties during stringification.
2const user = { id: 1, name: "Bob", password: "secret", role: "admin" };
3const safeJson = JSON.stringify(user, ["id", "name", "role"]);
4console.log(safeJson); // {"id":1,"name":"Bob","role":"admin"}
- 在此示例中,
password被排除,以便安全地输出日志。当你想为安全原因移除敏感信息时这很有用。
将 replacer 作为函数使用(用于值的转换和过滤)
如果需要更高的灵活性,可以提供一个函数来处理每个键和值。下面是将 Date 保留为 ISO 字符串的示例。
1// Use a replacer function to customize serialization.
2const data = { name: "Carol", joined: new Date("2020-01-01T12:00:00Z") };
3const jsonCustom = JSON.stringify(data, (key, value) => {
4 // Convert Date objects to ISO strings explicitly
5 if (this && this[key] instanceof Date) {
6 return this[key].toISOString();
7 }
8 return value;
9});
10console.log(jsonCustom); // {"name":"Carol","joined":"2020-01-01T12:00:00.000Z"}
- 使用函数类型的
replacer时,传入键和值,返回值会被用于最终的 JSON 输出。this指向父对象,因此也可以用于嵌套转换。
JSON.parse() 的基础用法
JSON.parse 会把 JSON 字符串转换回对象。请用 try/catch 包裹,以便处理无效的 JSON 数据。
1// Parse a JSON string into an object and handle parsing errors.
2const jsonText = '{"title":"Example","count":42}';
3try {
4 const parsed = JSON.parse(jsonText);
5 console.log(parsed.title); // Example
6} catch (err) {
7 console.error("Invalid JSON:", err.message);
8}- 如果字符串无效,会抛出异常,因此处理外部数据时应始终包括异常处理。
使用 reviver 自定义还原(还原 Date 的示例)
通过使用 JSON.parse 的第二个参数 reviver,可以进行值的还原。下面是将存储的字符串日期还原为 Date 对象的示例。
1// Use a reviver to turn ISO date strings back into Date objects.
2const jsonWithDate = '{"name":"Dana","joined":"2021-06-15T09:00:00.000Z"}';
3const obj = JSON.parse(jsonWithDate, (key, value) => {
4 // A simple check for ISO date-like strings
5 if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
6 return new Date(value);
7 }
8 return value;
9});
10console.log(obj.joined instanceof Date); // true
reviver会为每个键调用,返回值会被用于最终的对象中。请根据你的应用场景严格校验日期格式。
美化输出与 space 参数
为了可读性而缩进输出,可在 JSON.stringify 的第三个参数传入数字或字符串。
1// Pretty-print an object with 2-space indentation for readability.
2const config = { host: "localhost", port: 3000, debug: true };
3const pretty = JSON.stringify(config, null, 2);
4console.log(pretty);
5/* {
6 "host": "localhost",
7 "port": 3000,
8 "debug": true
9} */- 对于需要人工阅读的 JSON(如日志或配置文件输出),指定缩进会更易读。但请注意,这会增加数据体积。
如何处理循环引用
如果存在循环引用,JSON.stringify 会抛出 TypeError。常用的解决方法是通过 seen 集合创建 replacer,以避免循环引用。
1// Safely stringify objects that may contain circular references.
2function safeStringify(value) {
3 const seen = new WeakSet();
4 return JSON.stringify(value, (key, val) => {
5 if (val && typeof val === "object") {
6 if (seen.has(val)) return "[Circular]";
7 seen.add(val);
8 }
9 return val;
10 });
11}
12
13const a = { name: "A" };
14a.self = a;
15console.log(safeStringify(a)); // {"name":"A","self":"[Circular]"}
- 使用
WeakSet可以检测出循环引用,同时避免内存泄漏。遇到循环引用时,将返回"[Circular]"。你也可以用引用ID来代替"[Circular]"。
使用 toJSON 方法自定义序列化
如果对象上定义了 toJSON 方法,JSON.stringify 会使用其返回值。当你想为每种类型嵌入转换规则时,这很有用。
1// Define toJSON on a class to customize its JSON representation.
2class Point {
3 constructor(x, y) {
4 this.x = x;
5 this.y = y;
6 }
7 toJSON() {
8 // This will be used by JSON.stringify
9 return { x: this.x, y: this.y, type: "Point" };
10 }
11}
12
13const p = new Point(10, 20);
14console.log(JSON.stringify(p)); // {"x":10,"y":20,"type":"Point"}
- 通过
toJSON可以在对象级别定义序列化规则,比起使用replacer更本地化且直观。
JSON 的限制和注意事项
不能被转换为 JSON 的典型值有 undefined、函数、Symbol、BigInt 以及循环引用。还要注意数值精度问题(如大整数和 IEEE 浮点数舍入)。
1// Demonstrate values that can't be represented in JSON.
2const sample = {
3 a: undefined,
4 b: function () {},
5 c: Symbol("s"),
6 d: 9007199254740993n // BigInt (note: JSON.stringify will throw on BigInt)
7};
8// JSON.stringify will throw for BigInt and will drop undefined, functions, symbols in objects.
9try {
10 console.log(JSON.stringify(sample));
11} catch (err) {
12 console.error("Error during stringify:", err.message);
13}- 处理
BigInt时,可以转为字符串,或者用自定义replacer或toJSON来定义其表示方式。
安全性(处理不受信任的 JSON 时)
JSON.parse 本身是安全的,比 eval 更安全,但直接信任并使用解析出的对象(比如做属性访问或数据库查询)是危险的。务必校验并检查数据是否符合预期的结构。
1// Parse external JSON and validate expected properties before use.
2const external = '{"username":"eve","role":"user"}';
3const parsed = JSON.parse(external);
4if (typeof parsed.username === "string" && ["user","admin"].includes(parsed.role)) {
5 console.log("Safe to use parsed.username and parsed.role.");
6} else {
7 throw new Error("Invalid payload");
8}- 对于输入校验(结构校验),可以使用如
ajv这样的库来保障健壮性。
性能考虑
频繁序列化和反序列化大对象会加重 CPU 和内存负担。如有需要,可以只传递差异、使用二进制格式(如 MessagePack)或采用流方式(依次处理数据)。在浏览器环境下,可以使用 structuredClone (用于复制)或在 postMessage 中用可转移对象。
具体建议(简要)
你也可以考虑如下要点:。
- 对于日志,避免美化输出,使用单行 JSON 以减小体积。
- 频繁序列化的对象应预先精简,去除不必要的属性。
- 用
replacer最小化遍历并排除无关属性。
实用示例:API 请求的发送/接收流程
最后,以下是顺序演示如何安全地将对象转换为 JSON 发送到服务器,以及如何从响应中还原日期。
1// Prepare payload, stringify safely, send via fetch, and revive dates on response.
2async function sendUserUpdate(url, user) {
3 // Remove sensitive info before sending
4 const payload = JSON.stringify(user, (k, v) => (k === "password" ? undefined : v));
5 const res = await fetch(url, {
6 method: "POST",
7 headers: { "Content-Type": "application/json" },
8 body: payload
9 });
10
11 const text = await res.text();
12 // Reviver: convert ISO date strings back to Date
13 const data = JSON.parse(text, (key, value) => {
14 if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
15 return new Date(value);
16 }
17 return value;
18 });
19 return data;
20}- 在本例中,发送前排除了
password,收到后通过reviver还原日期。实际操作时,还需添加错误处理和超时处理。
总结
使用 JSON.stringify() 和 JSON.parse() 可以实现对象与字符串之间的转换。你可以通过 replacer 和 reviver 自定义转换过程,也可以在类中实现 toJSON 来单独控制每个对象的序列化。循环引用、BigInt、函数等不能直接处理,需要预先特殊处理。
务必验证外部输入,确保敏感信息不被记录到日志或传输中。若数据量较大,避免美化输出、考虑差分传输或使用二进制格式会更加高效。
您可以在我们的YouTube频道上使用Visual Studio Code跟随上述文章进行学习。 请也查看我们的YouTube频道。