问题背景
后端返回的数据是数组字符串,数组里的元素每个还是number类型,比如:a: "[1,2,79999999999999999]";
而前端需要的格式是数组,数组中的元素是字符串:a: ['1', '2', '79999999999999999'];
这两种格式正好相反,关键里面的元素是一个位数很多的id;
遇到问题
前端处理: 首先需要将字符串转成数组,执行JSON.parse(a), 输出: [1, 2, 80000000000000000]
此时,精度出现了问题,超过16位的数四舍五入了
原因
JavaScript中能精准表示的最大整数是 Math.pow(2, 53),数字太大就可能丢失精度,JS在处理比2^53大的数据时,精度会出现问题;
解决办法
- 后端修改返回数据:
- 其实是后端传的数据格式不符合通用逻辑,一种是返回前端所需要的数据格式:
a: ["1","2","79999999999999999"]; - 如果非要传字符串,那就应该返回
a: "1,2,79999999999999999",这样前端只需要a.split(',')就能解决, 不会引起精度问题;
-
前端通过正则匹配,给数组添加引号:
如果后端死活不改的话,第二种解决办法就是通过正则,在JSON.parse()前先通过正则,给数组中元素加上引号,这样再执行JSON.parse时就不会出现精度问题了,具体方法见下面代码。
const a= "[1,2,79999999999999999]"
JSON.parse(a.replace(/(-?\d+)/g, '"$1"'))
// 正负数都通用
// 上面正则的意思找到数字,给数字添加双引号
// 输出: ['1', '2', '79999999999999999']
3. 使用bigint数据类型(本项目没使用):
BigInt 是一种内置对象,它提供了一种方法来表示大于 2^53 - 1 的整数。这原本是 Javascript 中可以用 Number 表示的最大数字。BigInt 可以表示任意大的整数。
但是请注意:
对任何 BigInt 值使用 JSON.stringify() 都会引发 TypeError,因为默认情况下 BigInt 值不会在 JSON 中序列化。但是,如果需要,可以实现 toJSON 方法:
BigInt.prototype.toJSON = function() { return this.toString(); }
BigInt.prototype.toJSON = function() { return this.toString(); }