BigInt杂谈

最近遇到了一个要在js里处理uint64位数字的问题,折腾了一下,记录一下,有经验的小伙伴欢迎交流。

nodejs和后台通信的时候,经常会碰到后台需要uint64类型的字段,但是js里又无法表示这么大的数字,以前我们没办法,但是新版V8支持了BigInt,让我们看到了一线生机。比如

JSON.stringify(BigInt('11111111111111111111111111'));

但是,非常不幸报错了

JSON.stringify的时候无法对BigInt类型进行序列化。那。。。那么办?我们来分析一下这个问题,当我们通过TCP协议给后台发送数据的时候,我们发送的内容到底是什么?比如我们发送一个数字1。我们发送的数据如下图所示。

红框是我们设置的数据,其他的协议内容可忽略。当我们发送一个字符串1的时候,发送的数据如下图所示。

我们看到多了两个字符(是引号的ascii),顿时心生一计,当我们想发送一个大数(大于js能表示的整数)的时候,我们可以把两边的引号去掉,那么后台同学收到的就是一个数字了,并且是一个大数。但是这个方案实施起来无疑非常麻烦。但是却给了我们一丝思路。就是我们可以在序列化的时候做一些hack。首先我们来看看JSON.stringify的原理。假设我们执行JSON.stringify({“a”: “1”}),那么stringify的步骤大概是
1 定义一个空字符串str
2 str = ‘{’
3 str加上键名a变成 str = ‘{“a”:’
4 str加上a的值’{“a”:’ + ‘“1”’ = ‘{“a”: “1”’
5 ‘{“a”: “1”}’
我们发现就是一个字符串拼接的过程。而js里’a’ + ‘b’=‘ab’,那么我们就找到了去掉左右两侧字符的办法。那么我们怎么把一个BigInt对象在stringify的时候转成一个字符串呢?stringify给我们留了钩子toJSON。

BigInt.prototype.toJSON = function () {
    return this.toString();
}
JSON.stringify(BigInt('11111111111111111111111111'));

大功告成(奇怪的是chrome 76.0.3809.100 V8 7.6.303.29已经不需要这个hack了,但是我的nodejs v14.6.0 V8 8.4.371.19反而会报错)。在toJSON里返回字符串,V8会把两个字符串加起来,从而消除了BigInt值的两个引号,如果是直接传字符串,则V8是str = str + ‘“字符串”’。完成了给后台传BigInt,那么后台给我们传BigInt怎么办呢?首先我们从后台拿到的是一系列字符串,如果直接用JSON.parse的话,会怎样呢?

JSON.parse('{"a": 123456789123456789123456789}'

会变成

{ a: 1.2345678912345679e+26 }

我们看到a的值已经不一样了,因为V8会把他当做一般的数字,没有特殊处理,需要我们只能绕过V8,自己解析,已经有了现成的库(https://github.com/sidorares/json-bigint)。

const func = json_parse({useNativeBigInt: true})
func('{"p": 1111111111111111111111111111111111111}')

输出

{p: 1111111111111111111111111111111111111n}

完美!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值