一个排查了一天的BUG,你在摸鱼🐟吧!

站会

在一次日常站会上,组员们轮流分享昨天的工作进展。一个组员提到:"昨天我整天都在排查一个BUG,今天还得继续。"

出于好奇,我问:"是什么BUG让你排查了这么久还没解决呢?"

他解释说:"是关于一个数据选择弹窗的问题。这个弹窗用表格展示数据,并且表格具有选择功能。问题在于,编辑这个弹窗时,表格中原本应该显示为已选状态的数据并没有正确显示已选状态。"

我猜测道:"是不是因为表格中数据的主键ID是大数值导致的?"

他回答说:"大数值?我不太确定。"

我有些质疑地问:"那你昨天都是怎么排查的?需要花一整天的时间,难道是在摸鱼吗?"

"没有摸鱼,只是这个BUG真得有点难搞,那个什么是大数值?"

"行吧,姑且信你,我待会给你看看。"

排查

表格使用的是 Ant Design 4.0 提供的 Table 组件。我检查了组件的 rowKey 属性配置,如下所示:

js 复制代码
<Table rowKey={record => record.obj_id}></Table>

这表明表格行的 key 是通过数据中的 obj_id 字段来指定的。随后,我进一步查看了服务端返回的数据。

可以看到一条数据中的 obj_id 字段值为 "898186844400803840",这是一个18位的数值。

在ES6(ECMAScript 2015)之前,JavaScript没有专门的整数类型,所有的数字都被表示为双精度64位浮点数(遵循IEEE 754标准)。这意味着在这种情况下,JavaScript能够安全地表示的整数范围是从 <math xmlns="http://www.w3.org/1998/Math/MathML"> − 2 53 + 1 -2^{53} + 1 </math>−253+1到 <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 53 − 1 2^{53} - 1 </math>253−1(即-9,007,199,254,740,991到9,007,199,254,740,991)。可以简单地认为超过16位的数值就是大数值。

JavaScript中很多操作处理大数值时会导致大数值失去精度。比如 Number("898186844400803840")

可以看到 "898186844400803840""898186844400803800" 的区别在第16位后,从 40 变成 00 这就是大数值失去精度的表现。

在看一下表格的数据展示,如下图所示:

可以确定的是,从服务端返回的数据到在表格中的渲染过程是没有问题的。那么,可能出现问题的地方还有两个:一是在选择数据后,数据被传递到父组件的过程中;二是父组件将已选数据发送回选择数据组件的过程中。

定位

我检查了他将数据传递给父组件的逻辑代码,发现了一个可疑点。

在上述代码中,JSON.parse 被用来转换数据中的每个值。在这个转换过程中,如果 item[key] 是以字符串形式出现的数值,并且这个字符串能够被 JSON.parse() 解析为 JSON 中的数值类型,那么 JSON.parse() 将会把它转换为 JavaScript 的 Number 类型。

这种转换过程中可能会出现精度丢失的问题。因为一旦字符串表示的数值的位数超过16位后,在转换为 Number 类型时就无法保证其精度完整无损。

解决

我们通过正则表达式排除了这种情况,如下所示:

ts 复制代码
newItem[key] = typeof item[key] === 'string' && /^\d{16,}$/.test(item[key]) ? 
               item[key] : 
               JSON.parse(item[key]);

经过修改并重新验证,问题得到了解决,数据选择弹窗现在可以正确展示已选择状态。

反思

这个表面上不起眼的BUG为何花费了如此长的时间来排查?除了对大数值的概念不甚了解外,还有一个关键原因是对JavaScript中可能导致大数值失去精度的操作缺乏深入理解。

大数值通常由两种表示方式,一个是用数值类型表示,一个是字符串类型表示。

如果用数值类型表示一个大数值,而且你不能直接修改源代码或源数据,这种情况比较棘手,因为一旦 JavaScript 解析器处理这个数值,它可能已经失去了精度。

这种情况通常发生在你从某个源(比如一个API或者外部数据文件)接收到一个数值类型的大数值,如果数据源头不能修改,只能使用第三方库lossless-json、json-bigint来解决。

如果用字符串类型表示一个大数值,在JS中只要有把其转成Number类型的值就会失去精度,不管是显式转换还是隐式转换。

显式转换,比如 Number()parseInt()parseFloat()Math.floorMath.ceilMath.round等等。

隐式转换,比如除了加法外的算术运算符、JSON.parseswitch 语句、sort的回调函数等等。

相关推荐
susu10830189112 分钟前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿1 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡2 小时前
commitlint校验git提交信息
前端
虾球xz3 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇3 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒3 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员3 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐3 小时前
前端图像处理(一)
前端
程序猿阿伟3 小时前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
疯狂的沙粒3 小时前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript