Menu

JavaScript JSON 教程:JSON.stringify、JSON.parse 与常见陷阱

如何把 JavaScript 对象转换成 JSON 再转换回来:JSON.stringify、JSON.parse、replacer 和 reviver 钩子,以及哪些值经不起这一来一回。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

JSON 是文本,不是对象

JSON(JavaScript Object Notation)是一种用于交换数据的文本格式。它看起来像 JavaScript 对象字面量,但它其实是一个字符串:一串可以通过网络发送、保存到文件或粘贴进配置里的字符。

// A JavaScript object - a live value in memory.
const user = { name: "Rosa", age: 30 };

// JSON - a string of text that represents the same data.
const json = '{"name":"Rosa","age":30}';

两者看起来很像,所以很容易混淆。记住这个思维模型就不会搞错:对象存在于你的程序里;JSON 是它们离开程序时的样子。 你用两个函数在两者之间转换:JSON.stringify(对象 → 字符串)和 JSON.parse(字符串 → 对象)。

JSON.stringify:对象转字符串

把任何 JavaScript 值转换成它的 JSON 表示:

结果是一个没有空格的单行字符串:紧凑,非常适合通过网络传输。typeof 检查也证实了这一点:是 string,而不是 object。

调试时为了方便阅读,可以再传两个参数。第二个是 replacer(稍后会讲),传入 null 表示“全部包含”。第三个是缩进:

这是标准的格式化输出形式。用 2 或 4 个空格,和大多数工具的输出一致。

JSON.parse:字符串转对象

反过来:传入一个 JSON 字符串,得到一个 JavaScript 值。

解析之后,你面对的就是一个普通对象:点号访问、方括号访问、数组方法,统统都能用。

JSON.parse 很挑剔。下面这些都会抛出 SyntaxError:

JSON.parse("{name: 'Rosa'}");      // unquoted key, single quotes
JSON.parse('{"name": "Rosa",}');   // trailing comma
JSON.parse("// a comment\n{}");    // comments aren't allowed
JSON.parse("");                    // empty string

只要输入来自程序外部,比如 fetch 的响应、文件或用户输入,就把解析过程包在 try/catch 里:

经不起来回转换的值

JSON 支持六种值类型:字符串、数字、布尔值、null、数组和普通对象。JavaScript 中的其他一切都会被丢弃、被转换,或者直接出错。

会发生什么:

  • 函数和 undefined 会被悄悄地从对象中移除。在数组中它们会变成 null,因为 JSON 中的数组不能有空位。
  • Date 对象通过它的 toJSON 方法被序列化成 ISO 字符串。解析回来得到的是字符串,而不是 Date。
  • BigInt 会抛出 TypeError。JSON 数字没有与之对应的类型。
  • Map、Set 和循环引用同样无法直接处理。

要让 Date 在来回转换后保持不变,可以用 JSON.parse 的 reviver 函数:

reviver 会对每个键值对运行一次,让你在读入时转换值。

replacer:筛选要序列化的内容

JSON.stringify 的第二个参数可以控制输出中包含哪些内容。传入一个键名数组,就只保留这些键:

或者传入一个函数来实现任意逻辑:丢弃字段、遮盖值、即时转换:

返回 undefined 会移除这个键,返回其他任何值则会替换它。

用 toJSON 自定义

如果一个对象有 toJSON 方法,JSON.stringify 会调用它,并转而序列化它的返回值。Date 就是这样控制自己的格式的,你也可以使用同一个钩子:

对于希望无论谁来序列化都对外呈现一致结构的类来说,这非常好用。

深拷贝(老技巧和更好的方法)

多年来,JSON.parse(JSON.stringify(obj)) 一直是深拷贝普通对象的一行代码写法:

它是可行的,前提是对象只包含 JSON 安全的值。Date、Map 和函数都会让它出问题(见上文的来回转换问题)。

现代 JavaScript 有 structuredClone,它能处理 Date、Map、Set、类型化数组和循环引用:

能用 structuredClone 时就用它。把 JSON.parse(JSON.stringify(...)) 这个技巧留作备用,用来快速粗略地拷贝纯数据。

实际例子:获取并解析数据

你最常接触 JSON 的地方是 HTTP。fetch 不会替你解析 JSON,你需要在响应上调用 .json()(它实际上就是对响应体执行 JSON.parse):

发送 JSON 则反过来:用 JSON.stringify 处理请求体,并设置 Content-Type 请求头。

await fetch("/api/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Rosa", age: 30 }),
});

这两种模式涵盖了现实中绝大多数的 JSON 操作。

下一步:可选链

解析出的 JSON 常常有可选字段:一个可能不存在的 user.address.city,一个可能缺失的 response.data.items。可选链(?.)就是用来在不崩溃的情况下访问深层嵌套属性的,这就是下一页的内容。

常见问题

JSON 和 JavaScript 对象有什么区别?

JSON 是一种文本格式:一个看起来像 JavaScript 对象字面量、但规则更严格的字符串。键必须用双引号括起来,字符串必须用双引号,值只能是字符串、数字、布尔值、null、数组和普通对象。JavaScript 对象则是内存中的一个活的值,可以包含函数、undefined、Date 实例以及任何其他东西。

如何把 JavaScript 对象转换成 JSON?

调用 JSON.stringify(obj)。它会遍历对象并返回一个 JSON 字符串。传入第三个参数,比如 JSON.stringify(obj, null, 2),就能以 2 个空格缩进格式化输出。对象中的函数和 undefined 值会被丢弃,在数组中则会变成 null。

为什么 JSON.parse 会报错?

JSON.parse 很严格:末尾逗号、单引号、不带引号的键和注释都会导致 SyntaxError。只要输入来自网络请求、文件或用户,也就是任何可能不是合法 JSON 的地方,就把调用包在 try/catch 里。

JSON.stringify 会保留 Date 吗?

不会。Date 会变成一个 ISO 字符串,比如 "2026-01-15T10:30:00.000Z"。当你用 JSON.parse 把它解析回来时,得到的是这个字符串,而不是 Date 对象。如果需要 Date 实例,可以用 JSON.parse 的 reviver 参数把 ISO 字符串转换回来。

Coddy 编程语言插图

和 Coddy 一起学编程

开始