JS 大数溢出问题

2023-10-18 10:21:24 +08:00
 justdoit123
后端的大数到 JS 端 JSON.parse 之后,经常大数溢出。

这个问题之前一直是在后端看到一处,就 stringify 一处。现在实在是觉得烦了,想请教下各位有什么更好的做法?

查了些资料,这个溢出是在 JS 进程里 JSON.parse 的时候发生的,跟 JS 自身有关系,跟 JSON 没有关系。想着,在 JSON.parse 的时候,换成 BigInt 的 JSON.parse 。但是这其中也有两种策略:

1. 只要是整数,全部转成 BigInt ,不管实际会不会溢出。这样的好处是统一,坏处怕会有什么性能问题
2. 只有当一个整数会溢出的时候,才会转成 BigInt 。但是这样用的时候还需要做下判断,比较麻烦。

暂时想在内部后台系统里尝试下,浏览器兼容不成问题。有没有别的高招?

PS. 这个真是 JS 的天坑。
3800 次点击
所在节点    JavaScript
47 条回复
coala
2023-10-18 11:09:48 +08:00
Long 类型是吧, 其实我觉得很多项目 Long 当主键听没必要的。

全局 Long 转 JSON 为 String 类型。
justdoit123
2023-10-18 11:13:08 +08:00
@coala 同意。不过遗留项目,已成定局。
cheng6563
2023-10-18 11:14:17 +08:00
你这样理解,js 里面数值只有 double 类型,double 自然是放不下 bigint 的数据的
justdoit123
2023-10-18 11:16:38 +08:00
@jazzg62 请问下,如果这个数字要回传给后端你们怎么处理?也是让后端在 server 拿到后转成数字吗?
cheng6563
2023-10-18 11:18:28 +08:00
后端如果用得只是标准的 JSON 转换库的话,用 Long 类型自然就会出问题。
justdoit123
2023-10-18 11:19:35 +08:00
js 这个“缺陷”的原因我知道。

话说,js 未来是否能支持真正的 int 类型,面量就写成 类似 `97i32` 或者 `97i`之类的?
Martens
2023-10-18 11:26:18 +08:00
如果你后端用的 golang 的话,可以在相应的结构体 json tag 中添加 ,string 来实现序列化 json 时将 int64 转为字符串。
```go
type T struct {
ID int64 `json:id,string`
}
```
jazzg62
2023-10-18 11:29:32 +08:00
@justdoit123 传字符串给后端,后端可以转换的
iMouseWu
2023-10-18 11:37:40 +08:00
@justdoit123 在 VO 层做一下转 String ,其实成本也还好。
fiveStarLaoliang
2023-10-18 11:59:28 +08:00
我都是序列化时把所有的数字转为字符串, 然后前端自己处理,这样就不会出现精度传着传着丢了的情况了
wusheng0
2023-10-18 12:03:24 +08:00
前端 axios 的话,可以自定义 parser ,然后用上面说的 json-bigint 。

可以全局,也可以单个函数,用到的地方解析一下。

```typescript

/**
* 定义 parser
*/
export function bigIntTransformer(data: string) {
const jsonBig = jsonBigint({ storeAsString: true });

try {
return jsonBig.parse(data);
} catch {
return JSON.parse(data);
}
}

/**
* 接口使用
*/
export function createAccountApi(reqVo: AccountReqVo) {
return http.post(genApiUrl("/add"), reqVo, {
transformRequest: bigIntTransformer,
});
}
```
zhy0216
2023-10-18 12:22:00 +08:00
得用第三方 json parser 库
humbass
2023-10-18 12:48:49 +08:00
为避免此类问题,无论前后端,我们在计算后都以字符串的形式传递,包括前后端之间的交互
darkengine
2023-10-18 12:55:29 +08:00
先做出来再考虑性能问题吧
libook
2023-10-18 14:25:27 +08:00
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER

JS 里用的 Number 是由 ECMAScript 规范定义好的,使用双精度浮点型,而双精度浮点型是 IEEE 754-2019 定义的,有精度边界。
用数字之前先看一下是不是超过了 MAX_SAFE_INTEGER 就行了(相应的还有 MIN_SAFE_INTEGER ),ES 和 JS 里面已经提供了这个常量可以用来对比。

前端用 double 类型,后端也用 double 类型才算是合适;相应的后端如果用 int64 ,前端也得用 bigint 。使用其他语言也是一样的问题,就好比用 C 写的客户端使用 double 类型与用 int64 的后端通信。归根结底是数据类型一致可以直接避免所有问题。

唯一的问题是 JSON 支持的数据类型有限,比如不支持 bigint ,所以就需要前后端换成其他兼容的类型(比如字符串)来使用 JSON 传输,或者干脆不用 JSON 换其他交换格式。
JSON.parse()支持传入 reviver 函数来对 k/v 进行处理 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#description
当然也可以找一些现成的支持 bigint 类型的 json 序列化和反序列化库。
hesetiema
2023-10-18 14:41:44 +08:00
JS 最大安全整数 9007199254740991 ,9 千万亿,一般金额/毫秒什么的,应该是够用的。接口传字符串就行,提交时后端再转回整数应该也不麻烦吧,9 楼说得很明白。
mxT52CRuqR6o5
2023-10-18 14:48:04 +08:00
JSON 最初设计的就是 JS 的子集,真要说谁的问题也是其他语言的问题,强行把超精度的数塞到 JSON 里了,甚至我见过后端框架虽然可以大数序列化不报错,但在序列化过程就已经产生精度问题了,前端拿到的就已经是错误的值
RedNax
2023-10-18 15:15:23 +08:00
@justdoit123
> 话说,js 未来是否能支持真正的 int 类型,面量就写成 类似 `97i32` 或者 `97i`之类的?

不会,因为已经被 typedArray/wasm/asm.js 支持了,不可能再增加一种基本类型。
Huelse
2023-10-18 15:18:50 +08:00
前端应该没有这种大数的计算操作吧?那就应该转为字符串,只是显示就好

这个所谓的天坑也是 js 的性能优势之一
mysunshinedreams
2023-10-18 16:10:25 +08:00
这个基本是初级工程师大概率会面对的问题,我们 C 段交互一般大概率是 string 了,要不然多端共用一套接口的话,指不定什么时候就出现各种千奇百怪的问题了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/983013

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX