前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库JSON.stringify BigInt 报错 自定义序列化
JavaScript数据处理

JSON.stringify 碰到 BigInt 直接报错,怎么序列化才不丢精度?

JSON 没有 BigInt 类型,JSON.stringify({ id: 1n }) 会直接抛 TypeError。常见做法是用 replacer 把 BigInt 转成十进制字符串,接收端再按约定的字段转回来。不要先转成 Number,超过 Number.MAX_SAFE_INTEGER 的值会悄悄变成另一个数。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#数据处理
先看核心答案读代码示例
理解线索

先定好它在 JSON 里长什么样

  1. 默认行为遇到 BigInt 直接抛 TypeError,整个序列化失败
  2. 字符串编码replacer 把它转成十进制字符串,所有位数都保留
  3. 字段约定接收端知道哪些字段是 BigInt,才能安全转回

比 BigInt 报错更常见的坑是反方向:后端用 64 位整数做 ID,直接以数字形式返回,前端 JSON.parse 时就已经丢了精度,根本没机会报错。

核心回答

先记住这个答案

JSON 的数字没有「任意精度整数」这个概念,所以 JSON.stringify 遇到 BigInt 时选择直接抛 TypeError,而不是偷偷转成 Number。这是故意的:自动转换会让大于 2^53 - 1 的值变成另一个数,序列化看起来成功了,数据却错了。工程上的做法是用 replacer 把 BigInt 转成十进制字符串,在接口约定里写明这个字段是字符串形式的大整数,接收端再按字段名转回 BigInt。解析时不能把所有纯数字字符串都转成 BigInt,手机号、邮编也是纯数字字符串。

  • 默认遇到 BigInt 直接抛 TypeError
  • 转字符串不丢精度,转 Number 会丢
  • 转回 BigInt 要按字段约定,不能见数字字符串就转
  • 后端返回的超大数字 ID 应该用字符串传

用 replacer 转字符串,再按字段转回来

订单 ID 用 BigInt 存,要发给后端,再从后端取回来。

序列化和反序列化各写一个回调JavaScript
const order = { id: 9007199254740993n, count: 2 }

try {
  JSON.stringify(order)
} catch (err) {
  console.log(err.name) // 默认直接报错
}

const json = JSON.stringify(order, (key, value) =>
  typeof value === 'bigint' ? value.toString() : value
)
console.log(json)

// 只把约定好的 id 字段转回 BigInt
const back = JSON.parse(json, (key, value) => (key === 'id' ? BigInt(value) : value))
console.log(typeof back.id, back.id === order.id)
查看输出与解释
TypeError
{"id":"9007199254740993","count":2}
bigint true

replacer 会对每个属性调用一次,只处理 typeof value === 'bigint' 的值,其他原样返回。JSON 里 id 变成了字符串,所有位数都在。解析时用 reviver,只对 key === 'id' 的值调 BigInt(),往返之后和原值完全相等。 实际项目里字段多了,可以维护一份「哪些字段是 BigInt」的 schema,或者用 zod 这类校验库在解析后统一转换,比在 reviver 里写一堆 if 清楚。

为什么不能转成 Number

Number 是 64 位浮点数,能精确表示的最大整数是 Number.MAX_SAFE_INTEGER,也就是 2^53 - 1。超过它之后,相邻的整数会变成同一个 Number。

超出安全整数范围后的精度丢失JavaScript
const big = 9007199254740993n

console.log(Number(big)) // 末位变了
console.log(Number(big) === Number(big - 1n)) // 两个不同的 ID 变成同一个数
console.log(JSON.parse('{"id":9007199254740993}').id) // 后端返回裸数字也一样
console.log(Number.MAX_SAFE_INTEGER)
查看输出与解释
9007199254740992
true
9007199254740992
9007199254740991

第二行最危险:两个不同的订单 ID 转成 Number 后相等了,前端拿去查详情,查出来是另一个订单。 第三行是更常见的场景。后端用 Java 的 long 或者雪花算法生成 ID,直接以数字形式写进 JSON。前端 JSON.parse 时就已经按 Number 解析,精度在这一步就丢了,没有任何报错。所以接口约定里,超过安全范围的整数应该一律用字符串传,这要和后端一起定。

改 BigInt.prototype.toJSON 行不行
给 BigInt.prototype 加一个 toJSON 方法,JSON.stringify 就不会再报错了。但这是修改全局原型,整个页面所有地方的序列化都会被影响,第三方库、日志上报也都跟着变,出了问题很难查。除非是完全自己控制的应用,否则用局部 replacer 更安全。
回答前,多想一步

容易答错的地方

先 Number(bigint) 再序列化
这只是把报错消掉了,没有保证值正确。超出安全范围后,末位会变,不同的值会撞成同一个。这种 bug 在测试环境 ID 还小的时候完全发现不了。
把所有纯数字字符串都转成 BigInt
reviver 里写 /^\d+$/.test(value) ? BigInt(value) : value,手机号、验证码、邮编都会被转成 BigInt,展示和比较全出问题。转换必须按字段名或 schema 来。
试着用自己的话回答

面试官还会怎么问?

能不能让 JSON 里输出成数字而不是字符串?

较新的环境提供了 JSON.rawJSON(),在 replacer 里返回 JSON.rawJSON(value.toString()),输出就是不带引号的数字。解析时 reviver 的第三个参数有 context.source,能拿到原始数字文本再转成 BigInt。这是比较新的能力,目前只有部分环境支持,用之前先查兼容性。另外,接收端如果用普通 JSON.parse,还是会丢精度,所以两端都要配合。

axios 拿到的响应里 ID 已经丢精度了,怎么办?

axios 默认用 JSON.parse 解析响应,到你手里时已经丢了。可以在 transformResponse 里拿到原始文本,用 json-bigint 这类库解析;最根本的办法还是让后端把大整数 ID 以字符串返回。

为什么 JSON.stringify 对 BigInt 报错,对 NaN 却不报错?

NaN 和 Infinity 是规范里早就定好的,统一转成 null。BigInt 是 ES2020 才加的,委员会认为自动转换一定会有人丢精度,所以选择报错,逼调用方显式决定怎么编码。

从一道题,走向一组知识

把知识连起来

数据处理

JSON.stringify 遇到循环引用会报什么错?除了手动删属性还有什么办法?

BigInt 和循环引用是 JSON.stringify 会直接抛 TypeError 的两种情况,都得在序列化前想好怎么处理。

数据处理

JSON.stringify 遇到 NaN 和 Infinity 会怎样?为什么不报错?

同样是 JSON 表示不了的值,BigInt 会报错,NaN 和 Infinity 却悄悄变成 null,对比着记更清楚。

参考资料

  • JavaScript:数据处理参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 用 replacer 转字符串,再按字段转回来
  3. 为什么不能转成 Number
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑