问题描述
- 后端返回
{ spaceObject: { objectId: '1049564069045993472' } }
-
前端模版,使用的是 atpl 模版
<span id="test" data-id="<%= spaceObject.objectId %>"></span>
- 前端获取
objectId
的方式,const objectId = $('#test').data('id')
- 正常理解,我们获取到的
objectId
就是返回的1049564069045993472
,可是现实情况是这个objectId
是1049564069045993500
问题拆分
一,为什么从 dom 中获取的字符串会变成数字
查看 zepto
代码可知,由于通过 $('#test').data('id')
获取到的字符串 '1049564069045993472'
经过 deserializeValue
方法之后就变成数字了。
关键代码如下:
data: function (name, value) {
//[Opt:C]将原本在父级作用域的变量转移至局部变量
var capitalRE = /([A-Z])/g,
data = this.attr('data-' + name.replace(capitalRE, '-$1').toLowerCase(), value)
return data !== null ? deserializeValue(data) : undefined
},
// "true" => true
// "false" => false
// "null" => null
// "42" => 42
// "42.5" => 42.5
// "08" => "08"
// JSON => parse if valid
// String => self
function deserializeValue(value) {
var num
try {
return value ?
value == "true" ||
( value == "false" ? false :
value == "null" ? null :
!/^0/.test(value) && !isNaN(num = Number(value)) ? num :
/^[\[\{]/.test(value) ? $.parseJSON(value) :
value )
: value
} catch (e) {
return value
}
}
二,为什么数字跟 dom 中获取的不一致
由于javascript的能够保持精度的最大值是 9007199254740991
,所以由于上面那个数字大于这个最大安全数,所以会出现失去精度的问题。
引申
javascript 中精度丢失的几种情况
1. 简单的浮点数相加
0.1 + 0.2 !== 0.3 // true
0.1 + 0.2 === 0.3 // false
)
2. 大整数丢失精度
99999999999999999 === 100000000000000000
3. toFxied 有些情况下不会四舍五入
(12.235).toFixed(2) // 12.23
数字精度丢失问题原因分析
- 首先,javascript 中保持精度不丢失的数值是有个范围的,是在
Number.MIN_SAFE_INTEGER
和Number.MAX_SAFE_INTEGER
之间.Number.MAX_SAFE_INTEGER
=>9007199254740991
=>2的53次方-1
-
ECMA Section 8.5 - Numbers
Note that all the positive and negative integers whose magnitude is no greater than 253 are representable in the Number type (indeed, the integer 0 has two representations, +0 and −0).
- 计算机的二进制实现和位数限制有些数无法有限表示。就像一些无理数不能有限表示,如 圆周率 3.1415926...,1.3333... 等。JS 遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 bit。如图
* 1位用来表示符号位
* 11位用来表示指数
* 52位表示尾数
解决方案
- 使用 big.js库
-
如果是小数加减可以通过先将所有小数转化为整数(乘倍数),然后完成运算,最后缩小回去(除倍数)。
0.01 + 0.2 // 0.21000000000000002 (0.01 * 100 + 0.2 * 100) / 100 // 0.21
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。