根本原因是js的number类型基于ieee 754双精度浮点,安全整数上限为9007199254740991,超限大整数在json解析后会精度丢失或转科学计数法;统一方案是id类字段前后端均字符串化处理。

PHP 和 JavaScript 交互时数字变科学计数法或精度丢失
根本原因是 JS 的 Number 类型基于 IEEE 754 双精度浮点,安全整数上限为 Number.MAX_SAFE_INTEGER(9007199254740991)。一旦 PHP 后端返回超过该值的整型(比如订单 ID、用户 ID、时间戳毫秒),JS 解析后可能四舍五入或转成科学计数法,导致比对失败、接口报错。
常见现象:12345678901234567890 在 PHP 中是 int,在 JS 中变成 12345678901234567000 或 1.2345678901234567e+19。
- 后端用
json_encode($data)直接输出,不加干预,大整数原样进 JSON 字符串,但 JSJSON.parse()仍会转成 Number - 前端用
parseInt(res.id)或直接res.id === 12345678901234567890判等,必错 - MySQL 主键用
BIGINT UNSIGNED存 20 位数字,PHPfetch(PDO::FETCH_ASSOC)后直接 json_encode → 前端崩
统一方案:前后端约定「ID 类字段一律字符串化」
这不是妥协,而是事实标准。主流 API(微信、支付宝、GitHub、Stripe)全部把 ID 当字符串返回,OpenAPI spec 也推荐 type: string, format: int64。
- PHP 端:对需透出到前端的整型字段(如
id、user_id、order_no),在组装响应数组前显式转字符串:(string)$row['id'],而非依赖自动类型转换 - JavaScript 端:所有 ID 字段默认按字符串处理,禁止用
===跟数字比较;需要数值运算?先确认是否真有必要——绝大多数场景只需展示、传参、查表,字符串完全够用 - 若必须参与计算(如分页 offset),确保原始值来自可信来源(如 URL query 中的
page=2),且范围可控(),再用 <code>Number()或一元加号+
PHP json_encode 的坑:JSON_NUMERIC_CHECK 不能开
这个 flag 会让 json_encode() 把纯数字字符串(如 "123")强行转成数字,彻底破坏字符串化策略。
立即学习“PHP免费学习笔记(深入)”;
- ❌ 错误写法:
json_encode($data, JSON_NUMERIC_CHECK)—— 即使你写了'id' => (string)$id,也会被它“好心”还原成数字 - ✅ 正确写法:
json_encode($data),什么 flag 都不加;或只加JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES这类无害选项 - 验证方法:用
var_dump(json_encode(['id' => '12345678901234567890'])),输出应为{"id":"12345678901234567890"}(带引号),不是{"id":12345678901234567000}
额外注意:时间戳和金额的精度规则要分开定
ID 类字段走字符串路线,但时间戳和金额有不同约束:
- 时间戳(毫秒级):PHP 用
microtime(true) * 1000得到 float,直接 json_encode 会丢精度;应统一用(string)round(microtime(true) * 1000)转字符串,或退一步用秒级整数(time()) - 金额:永远用字符串或整数分(如 ¥19.99 存为
1999分),禁用 float 表示;PHP 返回'amount_cents' => 1999,JS 拿到后除以 100 显示,避免0.1 + 0.2 !== 0.3 - 小数计算(如统计平均值):如果必须前端算,用
decimal.js或big.js,别信parseFloat
前后端数字交互最易忽略的,是「以为自己传的是整数,其实 JSON 已经悄悄把它变成不可靠的浮点」。只要 ID、长整型、高精度时间戳这三类字段坚持字符串化,并禁用 JSON_NUMERIC_CHECK,95% 的精度问题就消失了。











