5.数值的扩展
1.二进制和八进制表示法
ES6提供了二进制和八进制数值的新的写法们分别用前缀0b(或0B)和0o(或0O)表示。
console.log(Number(0b111111)) //63 console.log(Number(0o77)) //63
从ES5开始,在严格模式中,八进制就不再允许使用前缀0表示,ES6进一步明确,要使用前缀0o表示。
1 (function(){ 2 console.log(0o77 === 077) //不报错 3 }) 4 (function(){ 5 'use strict'; 6 console.log(0o77===077) //Octal literals are not allowed in strict mode. 7 })
如果要将0b和0o前缀的字符串数值转为二进制,要使用Number方法。参考第一个代码。
2.数值分隔符
欧美语言中,较长的数值允许每三位添加一个分隔符(通常是一个逗号),增加数值的可读性。如:1000可以写为1,000。
ES2021,允许JavaScript的数值使用下划线(_)作为分隔符。
const text = 1_000_000; console.log(text===10**6) //true
这个数值分隔符并没有指定间隔的位数,也就是说,可以任意位数后添加一个分隔符。如:
console.log(1_0_00_000===10**6) //true console.log(1_0_0_0_0_0_0===10**6) //true
小数和科学计数法也可以使用数值分隔符。如:
console.log(0.0_01===0.001) //true console.log(1e11===1e1_1) //true
在使用分隔符时,需要注意以下几种情况。
-不能放在数值的最前面或者最后面
-不能两个或两个以上的分割符连在一起
-小数点的前后不能有分隔符
-科学计数法里,表示指数的e或E前后不能存在分隔符
除了十进制,其他进制也可以使用分隔符来增加可读性。
注意,分隔符不能紧跟着进制的前缀0b、0B、0o、0O、0x、0X。否则将会报错。如
console.log(0x123) //291 console.log(0x_123) //nvalid or unexpected token console.log(0_x123) //Numeric separator can not be used after leading 0
数值分隔符的使用指数为了便于可读和书写,对于JavaScript内部数值的存储和输出,并没有影像。如:
const text = 1_23 console.log(text) //123 console.log(text.toString()) //123
下面三个将字符串转成数值的函数,不支持数值分隔符。主要原因是语言的设计者认为,数值分隔符主要是无可编码时书写数值的方便,而不是为了处理外部输入的数据。如
-Number()
-parseInt()
-parseFloat()
const text = '1_23' console.log(Number(text)) //NaN console.log(parseFloat(text)) //1 parseFloat如果字符串第一个是数字就会一直往下解析直到不是数字,返回。 console.log(parseInt(text)) //1 parseInt用于将字符串参数作为有符号的十进制整数进行解析
3.Number.isFinite(),Number.isNaN()
ES6在Number对象上,新提供了Number.isFinite和Number.isNaN()两个方法。
Number.isFinite()用来检查一个数值是否为有限的,即不是infinity。
console.log(Number.isFinite(15)) //true console.log(Number.isFinite(0.1)) //true console.log(Number.isFinite(NaN)) //false console.log(Number.isFinite(Infinity)) //false console.log(Number.isFinite(-Infinity)) //false console.log(Number.isFinite('abc')) //false console.log(Number.isFinite('15')) //false console.log(Number.isFinite(true)) //false
注意,如果参数类型不是数值,Number.isFinite一律返回false。
Number.isNaN()用来检查一个值是否为NaN。
console.log(Number.isNaN(NaN)) //true console.log(Number.isNaN(0.1)) //false console.log(Number.isNaN('15')) //false console.log(Number.isNaN(true)) //false console.log(Number.isNaN(9/NaN)) //true console.log(Number.isNaN('true'/0)) //true console.log(Number.isNaN('true'/'true')) //true
如果参数类型不是NaN,Number.isNaN一律返回false。
他们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,在进行判断,而这两个新方法只对数值有效,Number.isFinite()对于非数值一律返回false,
Number.isNaN()还有对NaN才返回true,非NaN一律返回false。
1 console.log(Number.isNaN(NaN)) //true 2 console.log(isNaN(NaN)) //true 3 console.log(Number.isNaN('NaN')) //false 4 console.log(isNaN('NaN')) //true 5 6 console.log(Number.isFinite(6)) //true 7 console.log(isFinite(6)) //true 8 console.log(Number.isFinite('6')) //false 9 console.log(isFinite('6')) //true
4.Number.parseInt(),Number.parseFloat()
ES6将全局方法parseInt()和parseFloat(),移植到Number对象上,行为完全保持不变。
1 console.log(parseInt("12.34")) //12 2 console.log(parseFloat("12.34#")) //12.34 3 4 console.log(Number.parseInt("12.34")) //12 5 console.log(Number.parseFloat("12.34#")) //12.34 6 7 console.log(Number.parseInt === parseInt) //true 8 console.log(Number.parseFloat === parseFloat) //true
上述代码可发现,虽然移植到Number上,但是输出结果、操作是没有什么变化的。
5.Number.isInteger()
Number.isInteger()用来判断一个数值是否为整数(如果精度要求较高,不推荐使用)
console.log(Number.isInteger(12.1)) //false console.log(Number.isInteger(12)) //true console.log(Number.isInteger(12.0)) //true
值得注意的是12.0和12位一个值,所以判断整数为true。
如果参数不是数值,Number.isInteger返回false。
1 console.log(Number.isInteger()) //false 2 console.log(Number.isInteger('12')) //false 3 console.log(Number.isInteger(null)) //false 4 console.log(Number.isInteger(true)) //false
注意,由于JavaScript采用IEEE 754标准,数值存储为64位双精度格式,数值精度最多可以达到53个二进制位(一个隐藏位与52个有效位)。如果数值的精度超过这个限度,第54位及后面的位数就会被丢弃,这种情况下,Number.isInteger可能会误判。
console.log(Number.isInteger(3.0000000000000002
)) //true
上述代码可发现,Number.isInteger的参数明明不是整数,但是判定结果却显示为true。原因是因为小数位数的精度达到了小数点后16个十进制位,转成二进制位就超过了53个二进制位,导致最后的2被丢弃了。
类似的情况还有,当一个数值的绝对值小于Number.MIN_VALUE(5E-324),即小于JavaScript能够分辨的最小值,会被自动转为0.这是,Number.isInteger也会误判。
console.log(Number.isInteger(5E-325)) //true console.log(Number.isInteger(5E-324)) //false
上述代码中由于5E-325的值太小,会被自动转为0,故而返回true。
6.Number.EPSILON
ES6在number对象上面,新增一个极小的常量Number.EPSILON。根据规格,他表示1与大于1的最小浮点数之间的差。
对于64位浮点数来说,大于1的最小浮点数相当于二进制的1.00...001,小数点后面有连续51个零。这个值减去1之后,就等于2的-52次方。
console.log(Number.EPSILON) ///2.220446049250313e-16 console.log(Number.EPSILON.toFixed(20))//0.00000000000000022204 console.log(Number.EPSILON === Math.pow(2,-52)) //true
Number.EPSILON实际上是JavaScript能够表示的最小精度。误差如果小于这个值,就认为已经没有意义了,即不存在误差。
引入一个这么小的量的目的,在于为浮点数计算,设置一个误差范围。我们知道浮点数计算是不准确的。
console.log(0.1+0.2) //0.30000000000000004 console.log(0.1+0.2-0.3) //5.551115123125783e-17
上面的代码也说明了,为什么0.1+0.2与0.3得到的结果为false。
console.log(0.1+0.2==0.3) //false
Number.EPSILON可以用来设置“能够接受的误差范围”。比如,误差范围设置2的-50次方(即Number.EPSILON&Math.pow(2,2)),即如果两个浮点数的差小于这个值,我们认为这两个浮点数相等。
因此,Number.EPSILON的实质是一个可以接受的最小误差范围(在判断时可以写方法进行校验)。如下
function withinErrorMargin(left,right){ return Math.abs(left-right)<Number.EPSILON*Math.pow(2,2); } console.log(0.1+0.2 == 0.3) //false console.log(withinErrorMargin(0.1+0.2,0.3)) //true console.log(1.1+1.3 == 2.4) //false console.log(withinErrorMargin(1.1+1.3,2.4)) //true
7.安全整数和Number.isSafeInteger()
javaScript能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。
console.log(Math.pow(2,53)) //9007199254740992 console.log(9007199254740992) //9007199254740992 console.log(9007199254740993) //9007199254740992 console.log(Math.pow(2,53) == Math.pow(2,53)+1) //true
上述代码汇总,超出2的53次方后,一个数就不准确了。
ES6引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限。
console.log(Number.MAX_SAFE_INTEGER) //9007199254740991 console.log(Number.MIN_SAFE_INTEGER) //-9007199254740991 console.log(Number.MAX_SAFE_INTEGER == Math.pow(2,53)-1) //true console.log(Number.MIN_SAFE_INTEGER == Math.pow(-2,53)+1) //true
Number.isSafeInteger()则是用来判断一个整数是否落在了上面所说的范围之内。
console.log(Number.isSafeInteger("a")) //false console.log(Number.isSafeInteger(null)) //false console.log(Number.isSafeInteger(NaN)) //false console.log(Number.isSafeInteger(Infinity)) //false console.log(Number.isSafeInteger(-Infinity)) //false console.log(Number.isSafeInteger(3)) //true console.log(Number.isSafeInteger(1.2)) //false console.log(Number.isSafeInteger(9007199254740990)) //true console.log(Number.isSafeInteger(9007199254740992)) //false console.log(Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1)) // false console.log(Number.isSafeInteger(Number.MIN_SAFE_INTEGER)) // true console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER)) // true console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1)) // false
这个函数的实现很简单,就是跟安全整数的两个边界值进行比较。
Number.isSafeInteger = function (n){ return (typeof n === 'number' && Math.round(n) === n && Number.MIN_SAFE_INTEGER <= n && Number.MAX_SAFE_INTEGER >=n) }
实际使用这个函数时,需要注意。验证运算结果是否落在安全整数的范围内,不要只验证运算结果,而要同时验证参与运算的每个值。
console.log(Number.isSafeInteger(9007199254740993))//false console.log(Number.isSafeInteger(990)) //true console.log(Number.isSafeInteger(9007199254740993-990)) //true console.log(9007199254740993-990) //9007199254740002实际结果应该为9007199254740003
上述代码可以发现,我们在进行运算时,应该对参与运算的所有值都进行校验,而不是仅仅只是对结果进行校验。
9.Math对象的扩展
ES6在Math对象上新增了17个与数学相关的方法。所以这些方法都是静态方法,只能在Matj对象上调用。
(1.)Math.trunc()
Math.trunc方法用于去除一个数的小数部分,返回整数部分。
console.log(Math.trunc(4.1)) //4 console.log(Math.trunc(-4.1)) //-4 console.log(Math.trunc(5.9)) //5 console.log(Math.trunc(-5.9)) //-5 console.log(Math.trunc(0.1)) //0 console.log(Math.trunc(-0.1)) //-0
对于非数值,Math.trunc内部使用Number方法将其先转为数值。
console.log(Math.trunc('4.1')) //4 console.log(Math.trunc(true)) //1 console.log(Math.trunc(false)) //0 console.log(Math.trunc(null)) //0 console.log(Math.trunc(undefined)) //NaN console.log(Math.trunc("哈哈")) //NaN
(2.)Math.sign()
Math.sign方法用来判断一个数是否为正数、负数、还是零。对于非数值,会将其先转化为数值。其返回值为
参数为正数,返回+1
参数为负数,返回-1
参数为0,返回0
参数为-0,返回-0
其他值,返回NaN。具体实例如下
console.log(Math.sign(-5)) //-1 console.log(Math.sign(5)) //1 console.log(Math.sign(0)) //0 console.log(Math.sign('31')) //1 console.log(Math.sign(NaN)) //NaN console.log(Math.sign("哈哈")) //NaN
(3.)Math.cbrt()
Math.cbrt()方法用于计算一个数的立方根,对于非数值,回显将其转为数值,再进行计算。
console.log(Math.cbrt(-5)) //-1.709975946676697 console.log(Math.cbrt(5)) //1.709975946676697 console.log(Math.cbrt(0)) //0 console.log(Math.cbrt('31')) //3.1413806523913927 console.log(Math.cbrt(NaN)) //NaN console.log(Math.cbrt("哈哈")) //NaN
(4.)Math.clz32()
Math.clz32()方法将参数转为32位无符号整数的形式,然后返回这个32位值里面有多少个前导0。
console.log(Math.clz32(0)) //32 console.log(Math.clz32(1)) //31 console.log(Math.clz32(2)) //30 console.log(Math.clz32(0b01000000000000000000000000000000)) //1
上述代码中,0的二进制还是0,故有32个前导0;1的二进制为1,有31位前导0;2的二进制为10,有30位前导0;0b表示的是二进制的意思,故有1个前导0。
clz32这个函数来自于count leading zero bits in 32-bit binary representation of a number(计算一个数字的32位二进制的前导0)的个数的缩写。
左移运算符(<<)和Math.clz32方法直接相关,例下:
console.log(Math.clz32(1)) //31 console.log(Math.clz32(1<<1)) //30 console.log(Math.clz32(1<<2)) //29 console.log(Math.clz32(1<<3)) //28
对于小数来说,Math.clz32方法只考虑整数部分。如下:
console.log(Math.clz32(1.2)) //31 console.log(Math.clz32(1.6)) //31 console.log(Math.clz32(2.1)) //30 console.log(Math.clz32(2.6)) //30
对于空值和其他类型的值,使用Math.clz32会将他们先转换为数值,再进行运算。如下:
console.log(Math.clz32()) //31 console.log(Math.clz32(NaN)) //32 console.log(Math.clz32(null)) //32 console.log(Math.clz32(undefined)) //32 console.log(Math.clz32(true)) //31 console.log(Math.clz32(false)) //32 console.log(Math.clz32('1')) //31 console.log(Math.clz32('asdasd')) //32 console.log(Math.clz32('asdasd')) //32
(5.)Math.imul()
Math.imul方法是将两个32位带符号整数相乘,返回一个带符号的整数。
console.log(Math.imul(-1,-1)) //1 console.log(Math.imul(-1,5)) //-5 console.log(Math.imul(-5,6)) //-30 console.log(Math.imul(-5,5)) //-25
如果只考虑最后32位,大多数情况下计算都是准确的,即Math.imul(x,y)与x*y是一样的。但在超过32位时,超出部分会被过滤掉(JavaScript会将超出2的53次方的数据无法准确显示),使用这个方法却可以返回正确的最低位运算。如下:
console.log((0x7fffffff*0x7fffffff)|0) //0 console.log(Math.imul(0x7fffffff,0x7fffffff)) //1
上述代码,可以发现第一个两数相乘,正常来说最低位一定是1,但由于结果大于2的53次方,由于JavaScript无法显示超出精度的数据,故最低位为0,当使用Math.imul时,最低位依旧为1。
(6.)Math.fround()
Math.found()返回一个数的32位单精度浮点数的形式。
对于32位单精度格式来说,数值精度是24个二进制位,所以对于 -2的24次方 至 2的24次方 之间的整数(不含两个端点),返回结果与参数本身一致。
console.log(Math.fround(1)) //1 console.log(Math.fround(2)) //2 console.log(Math.fround(2**24-1)) //16777215 console.log(Math.fround(2**24)) //16777216 console.log(Math.fround(2**24+1)) //16777216
上述代码中,当参数>=2的24次方时,就会发现精度丢失。
对于NaN和Infinity,Math.fround()方法会返回原值。对于其他类型的数据,Math.fround()会将其转化为数值,然后在进行运算。如下:
console.log(Math.fround(NaN)) //NaN console.log(Math.fround(Infinity)) //Infinity console.log(Math.fround(11)) //11 console.log(Math.fround('11')) //11 console.log(Math.fround('asd')) //NaN console.log(Math.fround(null)) //0 console.log(Math.fround(undefined)) //NaN
Math.fround()方法的主要作用,是吧64位双精度浮点数转为32位单精度浮点数。如果小数超过24位二进制,这返回的数据就不和原始数据不同。
(7.)Math.hypot()
Math.hypot返回所有参数的平方和的平方根。例:
console.log(Math.hypot(3,4)) //5 console.log(Math.hypot(2,6)) //6.324555320336759 console.log(Math.hypot(2,2,2)) //3.4641016151377544 console.log(Math.hypot(2,2,null)) //2.8284271247461903 console.log(Math.hypot(2,2,undefined)) //NaN console.log(Math.hypot(2,2,NaN)) //NaN console.log(Math.hypot(2,2,'dd')) //NaN console.log(Math.hypot(2,2,'2')) //3.4641016151377544
如果参数不是数值,Math.hypot会先将其转为数值,再进行运算,如果参数其中一个不是数值,这最终结果为NaN。
(8.)Math.expm1()
Math.expm1()方法为e的x方-1,即Math.exp(x)-1。
console.log(Math.expm1(-1)); //-0.6321205588285577 console.log(Math.expm1(0)); //0 console.log(Math.expm1(1)); //1.718281828459045
对于没有部署这个方法的环境,可使用以下代码:
Math.expm1 = Math.expm1||function(x){ return Math.exp(x)-1 }
(9.)Math.log1p()
Math.log1p(x)方法返回1+x的自然对数,即Math.log(1+x),如果x<-1,则返回NaN。
console.log(Math.log1p(2)) //1.0986122886681096 console.log(Math.log1p(-1)) //-Infinity console.log(Math.log1p(-3)) //NaN console.log(Math.log1p('2')) //1.0986122886681096 console.log(Math.log1p('aa')) //NaN console.log(Math.log1p(null)) //0 console.log(Math.log1p(undefined)) //NaN console.log(Math.log1p(true)) //0.6931471805599453
对于没有部署这个方法的环境,可使用一下代码:
Math.log1p = Math.log1p||function(x){ return Math.log(1+x) }
(10.)Math.log10()
Math.log10(x)方法返回以10为底x的对数,当x<0时,返回值为NaN。如下
console.log(Math.log10(1)) //0 console.log(Math.log10(0)) //-Infinity console.log(Math.log10(-1)) //NaN console.log(Math.log10(NaN)) //NaN console.log(Math.log10(true)) //0 console.log(Math.log10(null)) //-Infinity console.log(Math.log10(undefined)) //NaN console.log(Math.log10('asd')) //NaN
对于没有部署这个方法的环境,可使用一下代码:
Math.log10 = Math.log10||function(x){ return Math.log(x)/Math.LN10 }
(11.)Math.log2()
Math.log2(x)方法返回以2为底x的对数,当x<0时,返回值为NaN。如下
console.log(Math.log2(1)) //0 console.log(Math.log2(2)) //1 console.log(Math.log2(4)) //2 console.log(Math.log2('4')) //2 console.log(Math.log2(null)) //-Infinity console.log(Math.log2(undefined)) //NaN console.log(Math.log2(NaN)) //NaN console.log(Math.log2('sa')) //NaN
对于没有部署这个方法的环境,可使用一下代码:
Math.log10 = Math.log10||function(x){ return Math.log(x)/Math.LN2 }
(12.)双曲函数方法
Math.sinh(x) 返回x的双曲正弦
Math.cosh(x) 返回x的双曲余弦
Math.tanh(x) 返回x的双曲正切
Math.asinh(x) 返回x的反双曲正弦
Math.acosh(x) 返回x的反双曲余弦
Math.atanh(x) 返回x的反双曲正切
10.指数运算符
ES2016 新增了一个指数运算符(**
)。
console.log(2**2) //4 console.log((-2)**2) //4 console.log((-2)**3) //-8
值得注意的是,这个运算符的特点是右结合,多个指数运算符连用时,从右边开始计算。
console.log(2**1**2) //2
指数运算符可以和等号结合,形成新的赋值运算符(**=)。
let a = 2; a**=2 //4 console.log(a**=2) //16 4的平方 console.log(a**=3) //4096 16的三次方
11.BigInt数据类型
(1.)简介
JavaScript所有数字都保存成64位浮点型,这给数值的表示带来了两大限制。一是数值的精度只能到53个二进制位(相当于16个十进制位),大于这个范围的整数,JavaScript是无法精确表示的,这使得JavaScript不适合进行科学和金融方面的精确计算。二是大于或等于2的1024次方的数值,JavaScpript无法表示,会返回Infinity。
console.log(2**1024) //Infinity
ES2020引入了一种新的数据类型BigInt(大整数),来解决这个问题。BigInt只能用来表示整数,且没有位数限制,也可以进行运算。为了跟Number类型区分,BigInt类型后面必须增加n。
const a = 123456789n; const b = 987456312n; console.log(a*b); //121908185557302168n const a1 = 123456789; const b1 = 987456312; console.log(a1*b1); //121908185557302180
BigInt与Number类型是不一样的,也不能进行比较。
console.log(123===123n) //false
typeof对于BigInt数据返回的类型为BigInt。
(2.)BigInt对象
JavaScript原生提供了BigInt对象,可以用作构造函数生成BigInt类型的数值。可以将其他类型的值转为BigInt,和Number()的区别在于,它转换的参数必须是可以正常转换为整数的(参数也不能为小数),否则会报错。
console.log(BigInt(123)) //123n console.log(BigInt('123')) //123n console.log(BigInt(false)) //0n console.log(BigInt(true)) //1n console.log(BigInt('asd')) //cannot convert asd to a BigInt console.log(BigInt(null)) //cannot convert null to a BigInt console.log(BigInt(undefined)) //cannot convert undefined to a BigInt console.log(BigInt(1.23)) //The number 1.23 cannot be converted to a BigInt because it is not an integer
BigInt对象继承了Object对象的两个实例方法。
BigInt.prototype.toString();
BigInt.prototype.valueof();
它还继承了Number对象的一个实例方法。
BigInt.prototype.toLocaleString()
此外,它还提供了三个静态方法。
BigInt.asUintN(width, BigInt)
: 给定的 BigInt 转为 0 到 2width - 1 之间对应的值。
BigInt.asIntN(width, BigInt)
:给定的 BigInt 转为 -2width - 1 到 2width - 1 - 1 之间对应的值。
BigInt.parseInt(string[, radix])
:近似于Number.parseInt()
,将一个字符串转换成指定进制的 BigInt。
(3.)转换规则
可以使用Boolean()、Number()、String()这三个方法,将BigInt可以转为布尔值、数值和字符串类型。
console.log(Boolean(1n)) //true console.log(Boolean(0n)) //false console.log(String(1n)) //'1' console.log(Number(1n)) //1
取反运算符(!)会将BigInt转为布尔值。
console.log(!0n) //true console.log(!1n) //false console.log(!2n) //false
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂