🚨 JSON.stringify 的 6 个隐藏坑:为什么你的数据序列化后"消失"了?

问题场景

后端同学甩来一句:"你传的 status 字段丢了,整个对象都是 null。" 你打开 DevTools 一查,明明打印出来是有值的。更诡异的是,同一个对象存 localStorage,刷新后有些字段直接不见了。

十有八九,你踩中了 JSON.stringify 的隐藏坑。

原因分析

JSON.stringify 不是"把对象变成 JSON 字符串"这么简单,它内部有一套序列化规则,很多值会被静默忽略 或偷偷转换,不报错、不警告,数据就这样无声无息地丢了。

6 个隐藏坑详解

坑 1:undefined / 函数 / Symbol 被静默丢弃

js 复制代码
const obj = {
  name: 'xuange',
  age: undefined,        // ❌ 被丢弃
  sayHi() {},            // ❌ 被丢弃
  [Symbol('id')]: 1,     // ❌ 被丢弃
  extra: null            // ✅ null 会被保留
};
JSON.stringify(obj); // '{"name":"xuange","extra":null}'

关键区别:对象属性 里的 undefined/函数/Symbol 整个键被丢弃;但数组元素 里的它们会变成 null:

js 复制代码
JSON.stringify([undefined, function(){}, 1]); // '[null,null,1]'

坑 2:NaN / Infinity 悄悄变成 null

js 复制代码
JSON.stringify({ a: NaN, b: Infinity, c: -Infinity });
// '{"a":null,"b":null,"c":null}'  --- 数值语义完全丢失

如果后端按 number 类型强校验,这里就是线上事故的源头。

坑 3:循环引用直接抛错

js 复制代码
const a = {}; a.self = a;
JSON.stringify(a); // ❌ TypeError: Converting circular structure to JSON

常见于:Vue 响应式对象(__ob__ 循环)、树形结构带 parent 指针。解决:用 replacer 过滤或维护一个 WeakSet 记录已访问对象。

坑 4:Date 不是"对象",是字符串

js 复制代码
JSON.stringify({ time: new Date() });
// '{"time":"2026-08-08T02:00:00.000Z"}' --- 自动调用了 toJSON()

序列化后时区信息丢失,跨时区后端解析容易差 8 小时。

坑 5:大数精度丢失

js 复制代码
JSON.stringify({ id: 9007199254740993 }); // '{"id":9007199254740992}'

超过 Number.MAX_SAFE_INTEGER(2^53-1)的整数会静默丢精度。雪花 ID、微信订单号都容易中招,必须用字符串传输。

坑 6:toJSON 的"越权"

只要对象上有 toJSON 方法,序列化结果就由它说了算,其他属性全部作废:

js 复制代码
const user = { name: '轩哥', toJSON() { return { name: '匿名' }; } };
JSON.stringify(user); // '{"name":"匿名"}'

解决方案:万能安全序列化

js 复制代码
function safeStringify(value, space) {
  const seen = new WeakSet();
  return JSON.stringify(value, (key, val) => {
    if (typeof val === 'bigint') return val.toString();      // 大数转字符串
    if (val instanceof Date) return val.toISOString();       // 明确时区
    if (typeof val === 'function' || typeof val === 'symbol') return undefined;
    if (typeof val === 'object' && val !== null) {
      if (seen.has(val)) return '[Circular]';                // 防循环引用
      seen.add(val);
    }
    return val;
  }, space);
}

业务侧约定:ID 一律字符串、时间统一 ISO 格式、可选字段用 null 不用 undefined,从源头消灭歧义。

要点总结

  • undefined/函数/Symbol 在对象里被丢弃、在数组里变 null,NaN/Infinity 变 null
  • 循环引用抛错,WeakSet + replacer 可兜底
  • Date 自动转 ISO 字符串、大整数丢精度 → 用字符串传输
  • 有 toJSON 时其他属性全失效,这是"特性"也是"坑"
  • 生产环境建议封装 safeStringify 统一处理边界值
相关推荐
Setsuna_F_Seiei10 小时前
前端转型 Agent 开发 06 之 Agent Memory 记忆系统(让 Agent 更智能,更懂你)
前端·agent·ai编程
zhangzeyuaaa10 小时前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
默_笙11 小时前
🌊 向量库和 ES 都查不出"关系",我只好给奶茶建了一张人脉网
前端·javascript
优选资讯11 小时前
标签打印软件怎么对接 Excel 数据
前端·excel
一个风轻云淡12 小时前
GCC 和 GDB命令简单解读
java·linux·前端
u01110267513 小时前
图片处理接口如何统一错误信息 前端提示与后端错误码设计
前端·状态模式
福兮说13 小时前
浏览器里把 iPhone 的 HEIC 转成 JPG:Chrome 解不开、转完大了七成、拍摄时间全丢,六个坑实测
前端·javascript·图像处理·chrome·ios·iphone·heic
libokaifa13 小时前
把语音模型塞进手机:sherpa-onnx 在 Android 上的 ASR / TTS
前端
deli00714 小时前
DLA 枝晶生长实测:5000 个粒子集体停摆之后,分形维数收敛到 1.70
前端
IT_陈寒14 小时前
Redis卡顿的锅,这次真不是大key的错
前端·人工智能·后端