为什么要使用Math.trunc去除小数位


ES2015 为 Math 对象扩展了 trunc 方法,旨在用于去除一个数的小数部分。
在此之前处理去除小数位这一项任务时,我们通常使用 parseIntMath.ceilMath.floor 这三个方法。

一个方法的一定是为了解决开发痛点而出现的,在此之前的这三个方法,各自存在哪些问题?

parseInt

parseInt 其返回值只有两种可能: 十进制整数NaN。 其去除小数位的工作原理本质上是遇到非数字字符,即 . 时停止解析,提取已解析部分。

它存在多种处理规则,处理数据时会得到许多不符合期望的结果。

Number.parseInt 是挂载到 Number 对象上的静态方法,而 parseInt 是全局对象上的方法,两者功能相同,这里仅对 parseInt 进行讨论。

转换非数字字符串

parseInt 在对目标字符串进行转换时,会从左到右查看,遇到非数字字符后停止,并提取数字部分。因此存在会转换非数字字符串的问题。

1
2
parseInt( "114.514g" ); // 114
Math.trunc( "114.514g" ); // NaN

因此对于小数的省略写法 .xxparseInt 会得到 NaN,而 Math.trunc 则会得到正确结果 0

1
2
parseInt( ".514" ); // NaN
Math.trunc( ".514" ); // 0

自动识别进制解析方式

默认不传递第二个参数时,parseInt 会尝试将目标字符串按照十进制解析。

但是当目标字符串以 0x0X 开头时,parseInt 会尝试将其按照十六进制来解析。

1
2
parseInt( "0xA0" ); // 160
parseInt( "0XA0MB" ); // 160,M 为无效字符,其后的不被解析,仅将 0xA0 解析为十进制

8 进制就得不到这种特殊待遇了,parseInt 在不同浏览器上对于 8 进制数字字符串的解析规则不同,
有的会自动识别 8 进制,有的则不会。

因此需要手动传入第二个参数来保证解析 8 进制。

1
2
parseInt( "010" ); // 10
parseInt( "010", 8 ); // 8

parseInt 还有一个特性,即当接受的第一个参数不是字符串时,会自动调用 toString 方法将其转换为字符串。
这对于 8 进制数字的字符串来说,会导致错误的结果。

1
2
parseInt( "011", 8 ); // 9
parseInt( 011, 8 ); // NaN,011 直接得到 9,然后被转换为字符串 "9","9" 不是 8 进制的合法数字,因此得到结果 NaN

错误转换科学计数法

当整数位数超过 22 位或小数位存在连续 6 个 0 时,将会自动以科学计数法展示,此时使用 parseInt 转换时会出现问题

1
2
3
4
5
6
7
let num = 3000000000000000000000.114; // 此时被直接解析为 3e+21
parseInt( num ); // 3
Math.trunc( num ); // 3e+21

num = 0.0000006; // 此时被直接解析为 6e-7
parseInt( num ); // 6
Math.trunc( num ); // 0

稍微提一嘴,parseFloat 并不会出现这种结果,而是直接返回科学计数法的结果

Math.ceil 与 Math.floor

Math.ceil 向上取整,可针对负数起到去除小数位的作用,正数则不可以。Math.floor 与其相反。

因此在 Math.trunc 出现之前,若使用 Math.ceilMath.floor 实现去除小数位,需要这样编写:

1
2
3
function getInteger( num ) {
return num < 0 ? Math.ceil( num ) : Math.floor( num );
}

Math.trunc

Math.trunc 则使得去除小数位的操作变得简单,不需要考虑正负号、进制、科学计数法等问题,直接使用即可。

1
2
Math.trunc( 114.514 ); // 114
Math.trunc( -114.514 ); // -114

需要注意的是,当第一个参数不是数字类型时,Math.trunc 会自动调用 Number() 方法将其转换为数字类型,

Number() 方法是不会将传统八进制字符串0xx)按照 8 进制解析的,使用 0oxx 方式的八进制字符串才会正常按照 8 进制解析,因此会有如下结果:

1
2
3
Math.trunc( 010 ); // 8
Math.trunc( "010" ); // 10,"010" 被 Number() 转换为 10
Math.trunc( "0o10" ); // 8