JSON序列化

JSON 序列化的行为特征、数据丢失点和常见问题。

#status / growing #type / concept

[!info] related notes

JSON 序列化

一句话定义

JSON.stringify() 将 JavaScript 值转换为 JSON 字符串,JSON.parse() 将 JSON 字符串还原为 JavaScript 值。它是跨语言数据交换的基础手段。

核心代码示例

// stringify
const obj = { name: 'Alice', age: 25, active: true };
const str = JSON.stringify(obj);
// '{"name":"Alice","age":25,"active":true}'

// parse
const restored = JSON.parse(str);
// { name: 'Alice', age: 25, active: true }

// 嵌套
const nested = { user: { name: 'Bob', scores: [90, 85] } };
JSON.stringify(nested);
// '{"user":{"name":"Bob","scores":[90,85]}}'

toJSON 方法

对象可以定义 toJSON() 方法来自定义序列化行为:

const user = {
  name: 'Alice',
  password: 'secret123',
  toJSON() {
    return { name: this.name }; // 不暴露密码
  }
};

JSON.stringify(user); // '{"name":"Alice"}'

replacer 和 reviver

// replacer:过滤/转换输出
const data = { name: 'Alice', password: 'secret', age: 25 };

// 白名单方式
JSON.stringify(data, ['name', 'age']);
// '{"name":"Alice","age":25}'

// 函数方式
JSON.stringify(data, (key, value) => {
  if (key === 'password') return undefined; // 过滤
  return value;
});

// reviver:转换解析结果
const text = '{"name":"Alice","date":"2024-01-01T00:00:00.000Z"}';
const parsed = JSON.parse(text, (key, value) => {
  if (key === 'date') return new Date(value);
  return value;
});
// { name: 'Alice', date: Date实例 }

数据丢失点

const problematic = {
  undef: undefined,    // 对象中:键被忽略
  fn: () => {},        // 对象中:键被忽略
  sym: Symbol('x'),    // 对象中:键被忽略
  date: new Date(),    // 变为 ISO 字符串(不再是 Date)
  regex: /test/g,      // 变为 {}
  map: new Map(),      // 变为 {}
  set: new Set(),      // 变为 {}
  nan: NaN,            // 变为 null
  inf: Infinity,       // 变为 null
};

JSON.stringify(problematic);
// '{"date":"2026-05-27T...","regex":{},"map":{},"set":{},"nan":null,"inf":null}'
// 数组中的特殊情况
JSON.stringify([undefined, () => {}, Symbol('x')]);
// '[null,null,null]' — 不是忽略,而是替换为 null

循环引用

const circular = { name: 'Alice' };
circular.self = circular;

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

解决方案:

// 方法一:replacer + WeakMap
function safeStringify(obj) {
  const seen = new WeakSet();
  return JSON.stringify(obj, (key, value) => {
    if (typeof value === 'object' && value !== null) {
      if (seen.has(value)) return '[Circular]';
      seen.add(value);
    }
    return value;
  });
}

BigInt 边界

JSON.stringify({ n: 123n });
// TypeError: Do not know how to serialize a BigInt

// 解决:自定义 replacer
JSON.stringify({ n: 123n }, (key, value) =>
  typeof value === 'bigint' ? value.toString() : value
);
// '{"n":"123"}'

性能

  • JSON.parse / JSON.stringify 底层由 C++ 引擎实现,对纯数据对象非常快。
  • structuredClone 快 2-5 倍(纯数据场景)。
  • 大对象场景:先 stringifyparse 仍然是最常用的深拷贝手段。

Web 场景为什么没问题

在 Web 后端(如 Node.js 接口),你 return 一个带有方法或 Getter 的 ORM 模型实例,HTTP 框架底层会调用 JSON 序列化。方法被静默丢弃了,前端刚好只拿到了纯数据,大家”相安无事”。

创建于 2026/2/21 更新于 2026/5/27