目录
一、Number数据对象的ES6新方法
1.Number.isFinite()
用来检查一个数值是否为有限的(finite),即不是Infinity
。注意,如果参数类型不是数值,Number.isFinite
一律返回false
。
Number.isFinite(15); // true
Number.isFinite(0.8); // true
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false
Number.isFinite(-Infinity); // false
Number.isFinite('foo'); // false
Number.isFinite('15'); // false
Number.isFinite(true); // false
2.Number.isNaN()
用来检查一个值是否为NaN
。如果参数类型不是NaN
,Number.isNaN
一律返回false
。
NaN 即 Not a Number ,不是一个数字。 在 JavaScript 中,整数和浮点数都统称为 Number 类型 。除此之外,Number 类型还有一个很特殊的值,即 NaN 。
NaN的产生:
(1)表达式计算,一个表达式中如果有减号 (-)、乘号 (*) 或 除号 (/) 等运算符时,JS 引擎在计算之前,会试图将表达式的每个分项转化为 Number 类型(使用 Number(x) 做转换)。如果转换失败,表达式将返回 NaN 。
(2)类型转换,直接使用 parseInt,parseFloat 或 Number 将一个非数字的值转化为数字时,表达式返回 NaN ;其中Number 转换的是整个值,而不是部分值;parseInt 和 parseFloat 只转化第一个无效字符之前的字符串。
// (2)使用 Number(x) 做转换,x是以下的表达式,来产生NaN 100 - '2a'; // NaN '100'/ '20a'; // NaN '20a'* 5 ; // NaN undefined - 1; // NaN, 因为Number(undefined)结果是NaN [] * 20 ; // 0, Number([]) == 0 null- 5; // -5, Number(null) == 0 'true'; // NaN true-5; // -4, Number(true)===1 false; // 0, Number(false)===0 'abc'- 3 // NaN // (2)使用数据类型转换产生NaN parseInt('abc') // NaN parseFloat('abc') // NaN Number('abc') // NaN Number('123abc'); // NaN parseInt('123abc'); // 123 parseInt('123abc45'); // 123 parseFloat('123.45abc');// 123.45
// isNaN全局函数先将里面的参数进行Number转化,再进行比较
isNaN(NaN) // true
isNaN("NaN") // true
isNaN("true") // true
isNaN(true) // false
isNaN(null) // false
isNaN([]) // false
// Number.isNaN函数直接进行比较,不转化。
Number.isNaN(NaN) // true
Number.isNaN("NaN") // false
Number.isNaN(1) // false
isNaN("true") // false
区别:
Number对象方法Number.isFinite()和
和Number.isNaN()跟
全局方法isFinite()
和isNaN()
的区别在于传统方法先调用Number()
将非数值的值转化为数值,再进行判断,而这两个新方法不进行数据类型转化而是直接进行比较,Number.isFinite()
对于非数值一律返回false
, Number.isNaN()
只有对于NaN
才返回true
,非NaN
一律返回false
。
3.Number.parseInt()和Number.parseFloat()
ES6 将全逐步减少全局性方法,使得语言逐步模块化。局方法parseInt()
移植到Number
对象上面,行为完全保持不变。这样做的目的,是只转化第一个无效字符之前的字符串。
// ES5的写法
parseInt('12.34') // 12
parseFloat('123.45#adc') // 123.45
parseFloat('a123.45#') // NaN
// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
parseFloat('d123.45#') // NaN
// 实际上是一样的,只是为了逐步减少全局性方法,使得语言逐步模块化。
Number.parseInt === parseInt // true
Number.parseFloat === parseFloat // true
4.Number.isInteger()
Number.isInteger()
用来判断一个数值是否为整数。注意:JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值。如果参数不是数值,Number.isInteger
返回false
。
Number.isInteger(25) // true
Number.isInteger(25.1) // false
Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger() // false
Number.isInteger(null) // false
Number.isInteger('15') // false
Number.isInteger(true) // false
二、数值的精度
1.精度丢失的一些典型问题
- 两个简单的浮点数相加(有时候会丢失)
0.1 + 0.2 != 0.3 // true
- 大整数运算。超过
9999999999999999 == 10000000000000001 // true
- 误断浮点数为整数
Number.isInteger(3.0000000000000002) // true 浮点数误判为整数 Number.isInteger(5E-324) // false 判断正确 Number.isInteger(5E-325) // true 小数误判成整数
2.JS 数字丢失精度的原因
JS 遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 bit。由于计算机不能表示无限数字(包括小数和整数),所以进行二进制编码的时候会有位数限制。
根据国际标准 IEEE 754,JavaScript 浮点数的64个二进制位,从最左边开始,是这样组成的。
- 第1位:符号位,
共1位,0
表示正数,1
表示负数 - 第2位到第12位:指数部分,共11位
- 第13位到第64位:小数部分(即有效数字),共52位
符号位决定了一个数的正负,指数部分决定了数值的大小,小数部分决定了数值的精度。
浮点数只能模仿十进制进行四舍五入了,但是二进制只有 0 和 1 两个,于是变为 0 舍 1 入。这即是计算机中部分浮点数运算时出现误差,丢失精度的根本原因。
大整数的精度丢失和浮点数本质上是一样的,尾数位最大是 52 位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。大于 9007199254740992 的可能会丢失精度。
3.解决方案
对于整数,前端出现问题的几率可能比较低,毕竟很少有业务需要需要用到超大整数,只要运算结果不超过 Math.pow(2, 53) 就不会丢失精度。
对于小数,前端出现问题的几率还是很多的,尤其在一些电商网站涉及到金额等数据。解决方式:
-
数据展示类,
使用toPrecision(n)函数限定n位效数字(
经验n一般选12就能解决掉大部分0001和0009问题,而且大部分情况下也够用了,如果你需要更精确可以调高),但是返回string,所以再使用parseFloat()函数化成数字
凑整并parseFloat
()函数 转成数字后再显示 -
数据运算类,把小数放到位整数(乘倍数),再缩小回原来倍数(除倍数)
// 当你拿到 1.4000000000000001 这样的数据要展示时,建议使用 toPrecision 凑整并 parseFloat 转成数字后再显示
parseFloat(1.4000000000000001.toPrecision(12)) === 1.4 // True
// 对于运算类操作,如 +-*/,就不能使用 toPrecision 了。正确的做法是把小数转成整数后再运算。
(0.1*10+0.2*10)/10 // 0.3
0.1+0.2 // 0.30000000000000004
4.安全整数范围Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER
; Number.isSafeInteger()
JavaScript 能够准确表示的整数范围在-2^53
到2^53
之间(不含两个端点),超过这个范围,无法精确表示这个值。ES6 引入了Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
这两个常量,用来表示这个范围的上下限。
Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1 // true
Number.MAX_SAFE_INTEGER === 9007199254740991 // true
Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER // true
Number.MIN_SAFE_INTEGER === -9007199254740991 // true
Number.isSafeInteger()
则是用来直接判断一个整数是否落在这个范围之内。这个函数的实现很简单,就是跟安全整数的两个边界值比较一下。注意:验证运算结果是否落在安全整数的范围内,不要只验证运算结果,而要同时验证参与运算的每个值。
Number.isSafeInteger('a') // false
Number.isSafeInteger([]) // false
Number.isSafeInteger(null) // false
Number.isSafeInteger(NaN) // false
Number.isSafeInteger(Infinity) // false
Number.isSafeInteger(-Infinity) // false
Number.isSafeInteger(3) // true
Number.isSafeInteger(3.0000) // true
Number.isSafeInteger(1.2) // false
Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1) // false
Number.isSafeInteger(Number.MIN_SAFE_INTEGER) // true
Number.isSafeInteger(Number.MAX_SAFE_INTEGER) // true
Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1) // false
四、Math对象的拓展
ES6 在 Math 对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用。
1.Math.trunc()
方法用于去除一个数的小数部分,返回整数部分。对于非数值,Math.trunc
内部使用Number
方法将其先转为数值。对于空值和无法截取整数的值,返回NaN
。
Math.trunc(4.9) // 4
Math.trunc(-4.9) // -4
Math.trunc(-0.1234) // -0
2.Math.sign
方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
它会返回五种值。
- 参数为正数,返回
+1
; - 参数为负数,返回
-1
; - 参数为 0,返回
0
; - 参数为-0,返回
-0
; - 其他值,返回
NaN
。
如果参数是非数值,会自动转为数值。对于那些无法转为数值的值,会返回NaN
。
3.Math.cbrt()
方法用于计算一个数的立方根。对于非数值,Math.cbrt()
方法内部也是先使用Number()
方法将其转为数值。
4.Math.clz32()
方法将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0。对于小数,Math.clz32
方法只考虑整数部分。对于空值或其他类型的值,Math.clz32
方法会将它们先转为数值,然后再计算。
5.Math.imul
方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。
6.Math.fround
方法返回一个数的32位单精度浮点数形式。对于 NaN
和 Infinity
,此方法返回原值。对于其它类型的非数值,Math.fround
方法会先将其转为数值,再返回单精度浮点数。
7.Math.hypot
方法返回所有参数的平方和的平方根。如果参数不是数值,Math.hypot
方法会将其转为数值。只要有一个参数无法转为数值,就会返回 NaN。
8.Math.expm1(x)
返回 ex - 1,即Math.exp(x) - 1
。
9.Math.log1p(x)
方法返回1 + x
的自然对数,即Math.log(1 + x)
。如果x
小于-1,返回NaN
。
10.Math.log10(x)
返回以 10 为底的x
的对数。如果x
小于 0,则返回 NaN。
11.Math.log2(x)
返回以 2 为底的x
的对数。如果x
小于 0,则返回 NaN。
12.Math.sinh(x)
返回x
的双曲正弦(hyperbolic sine)
13.Math.cosh(x)
返回x
的双曲余弦(hyperbolic cosine)
14.Math.tanh(x)
返回x
的双曲正切(hyperbolic tangent)
15.Math.asinh(x)
返回x
的反双曲正弦(inverse hyperbolic sine)
16.Math.acosh(x)
返回x
的反双曲余弦(inverse hyperbolic cosine)
17.Math.atanh(x)
返回x
的反双曲正切(inverse hyperbolic tangent)
ES2016 新增了一个指数运算符(**
)
五、BigInt 数据类型
JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。二是大于或等于2的1024次方的数值,JavaScript 无法表示,会返回Infinity
。
ES2020 引入了一种新的数据类型 BigInt(大整数),来解决这个问题,这是 ECMAScript 的第八种数据类型。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。
为了与 Number 类型区别,BigInt 类型的数据必须添加后缀n
。BigInt 同样可以使用各种进制表示,都要加上后缀n
。BigInt 与普通整数是两种值,它们之间并不相等。typeof
运算符对于 BigInt 类型的数据返回bigint
。BigInt 可以使用负号(-
),但是不能使用正号(+
),因为会与 asm.js 冲突。
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true 超过 53 个二进制位的数值,无法保持精度
Math.pow(2, 1024) // Infinity 超过 2 的 1024 次方的数值,无法表示
const a = 2172141653n;
const b = 15346349309n;
a * b // 33334444555566667777n BigInt 可以保持精度
Number(a) * Number(b) // 33334444555566670000 普通整数无法保持精度
1234 // 普通整数
1234n // BigInt
42n === 42 // false
typeof 123n // 'bigint'
-42n // 正确
+42n // 报错