正如《JSON实战》书中所言 —— “JSON已经成为 RESTful 接口设计中的事实标准”,作为一个写程序的,我们很难避免与JSON打交道。
JSON从本质上讲就是一类字符串,所以在第二章(“在 JavaScript 中使用 JSON”)里一开始就向我们介绍了JSON的序列化 / 反序列化操作。用JSON.stringify() 将信息序列化为 JSON(字符串);用JSON.parse() 将 JSON 反序列化为 JavaScript 可以理解的数据结构。
例如我们在node.js控制台或者chrome控制台里输入:
JSON.stringify({"数字": 12345678901234567890})
会得到:
'{"数字":12345678901234567000}'
再例如,输入:
JSON.parse('{"数字":12345678901234567890}')
会得到
{ '数字': 12345678901234567000 }
看看,JSON的序列化和反序列化就是如此简单!
但是,如果我们仔细的看看序列化和反序列化的结果,就会喊出WTF —— 我明明输入的数字是 12345678901234567890,怎么得到的却是12345678901234567000 ?😨
为什么会出现这样的结果?其实当你用的数字比较小的时候,是碰不上这个问题的,只有当你用的数字足够大(超过 Number.MAX_SAFE_INTEGER),或者足够小(小于 Number.MIN_SAFE_INTEGER)的时候,这个问题才会浮现,按照MDN上的描述是这样的 ——
形成这个数字的原因是 JavaScript 在 IEEE 754中使用double-precision floating-point
format numbers 作为规定。在这个规定中能安全的表示数字的范围在-(253 - 1) 到 253 - 1之间.
怎么办?
是否要解决这个问题取决于你的业务场景。
如果你确信你的程序不会涉及到上面那么大的数,那就放心的使用JSON.parse和JSON.stringify好了(真是废话!😄)
后端之间传递
如果你只是在后端使用JSON,那么选择方案还是挺多的,比如node.js里有一些库 lossless-json、json-bigint 可以解决精度的问题。再比如python就不存在这个问题(所以没人用javascript去做科学计算啊 😓)。
前后端之间传递
如果要在前端进行一些交互,比如让用户输入数字再转成JSON传递给后端,那么问题就出现了:怎么保证前端输入的数字在前端被转为JSON的时候不走样呢?我找了一圈之后发现答案其实挺简单,那就是你把在前端输入的数字就当成一个字符串,转成JSON就不会走样。
在JS控制台里输入:
JSON.stringify({"数字": "12345678901234567890"})
得到:
'{"数字":"12345678901234567890"}'
当这个写成字符串的JSON被传递到后端以后,根据后端用的语言不同,有不同的处理方法。
例如:
Python
python做后端的只要直接把数字字符串转成需要的数字类型即可,例如:
int(json.loads('{"数字":"12345678901234567890"}')['数字'])
得到
12345678901234567890
Node.js
可以利用 decimal.js 、bignumber.js 这样的库把 字符串转为"big number" 再进行计算:
var BigNumber = require('bignumber.js');
数字 = BigNumber('12345678901234567890');
数字.plus('987654321')
小结
JSON在Javascript的序列化与反序列化当中存在的数字精度其实是一个比较常见的问题,如果你在使用JS处理JSON数字时得到了一些莫名其妙的结果,请考虑一下是不是遇到了这个问题。