Jison解决JS处理后端返回的Long型数据精度丢失问题

在前端页面展示数据的时候,通常都需要处理来自后端的json数据。通常这个过程都是非常简单的,比如通过jQuery的ajax。但是如果服务器传来的json中包含一个很大的整数,如 { "id": 296675198462066688 } ,那么接受后会发现变成了 { id: 296675198462066700 } 。

简单的解决方法,让后端传给你string类型
但是后端的人会说:你们前端怎么显示个long类型都搞不定!
复制代码

问题原因

js是弱类型语言,所有的数字类型统称为Number类型,不区分int、long、double等。而Number是根据IEEE 754标准中的double来实现的,即所有的Number类型都是64位双精度实型。segmentfault上提供了一个对 IEEE 574标准 非常友好的讲解,这里不再讲述。

js内置有32位整数,而number类型的安全整数是53位。如果超过53位的,你不能用json传递,需要用其他数据类型,比如字符串,或拆分成两个数据字段。

解决思路

GitHub开源项目—— Jison,号称“bison in javascript”,通过它可以实现对后端返回数据的重新定义一个自己的json parser。

在拿到接口请求返回的数据的时候,不用json自带的那个parse方法,而是通过自己定义了一个json转换方法,然后再response给前端,这样一来前端拿到的数据就是一个处理过的json数据。

自定义 json parser

首先使用Node安装Jison

npm install jison -g
复制代码

同时在 lib 目录下提供了 cli.js 来生成我们想自定义的parser。参数有两个(详见cli.js代码), cli.js grammaFile lexFile ,grammaFile是语法文件,lexFile是词表文件。

下面就是怎么写这两个文件了,可以通过 文档 来学习一下这类文件的语法。如果不想这么麻烦,还可以在jison作者的另一个项目—— jsonlint 里面找到,在github中该项目的src目录下提供了jsonlint.y(grammaFile)和jsonlint.l(lexFile)两个文件。使用这两个文件可以直接生成jsonlint.js,放到网页中当json parser来使用。

这里我们以修改jsonlint里面的两个文件为例生成我们所需要的json parse

我们的目的是让一些会丢失精度的整数被保留下来,最好的方法是:当整数超过了安全范围的时候,使用字符串表示。我们可以通过修改jsonlint.y来达到这个目的。

原本对JSONNumber的定义是

JSONNumber
    : NUMBER
        {$$ = Number(yytext);}
    ;
复制代码

在这里yytext是要进行解析的原始数据,$$是结果。我们可以修改成

JSONNumber
    : NUMBER
        {$$ = yytext == String(Number(yytext))? Number(yytext): yytext;}
    ;
复制代码

==> 生成我们要的 jsonlint.js:

git clone git://github.com/zaach/jsonlint.git
cd src
jison jsonlint.y jsonlint.l
复制代码

引入至项目

这里以Vue项目为例: 1、将自定义的 jsonlint.js 放到 static 目录下

static.jpg

2、在 index.html 中引入

<script src="./static/jsonlint/jsonlint.js"></script>
复制代码

3、在我们请求的返回数据中,做一层拦截转换 此处以 axios 的实现方法为例:

// transformResponse 选项允许我们在数据传送到 `then/catch` 方法之前对数据进行改动
axios.defaults.transformResponse = [
  function(data) {
    return jsonlint.parse(data)
  }
]
复制代码

总结

综上,通过自定义JSON转化避免long类型数据溢出,可以实现long类型数据在前端正常显示。

注意: 这个方法的确可以实现前端拿到的数据不出现精度丢失问题,但是再浏览器中的Preview上查看数据还是一个丢失的错误数据,这个是因为浏览器它用的还是自己原始的那个json parse方法。

后记: 小伙伴们,如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果觉得本文还不错,记得点个赞哦! 本文首发地址为: Vae's Blog

猜你喜欢

转载自juejin.im/post/5c51526fe51d455047338a2a