第1章 数据类型 第3节 JavaScript数值类型(2)
JavaScript数值
# 概述
数值型作为JavaScript中一个基本数据类型在程序中是不可或缺的,程序中的逻辑实现、条件判断都直接或间接地需要数值才能实现。比如说对数组的操作,循环语句的实现,没有数值型这种数据类型是办不到的。当然,这只是指这种数据类型在程序中的运用。谈到实际的项目,像金融这一类的网站或应用,如果对数值型这个数据类型没有充分的了解,做出的成品是很难有质量保证的,其所表示的数据准确性也会饱受怀疑,在对互联网软件产品质量要求越来越高的今天,这样的产品是达不到上线要求的。
在JavaScript中,所有的数字都是以 64位浮点数的形式存储的,就算是表面上看到的是一个整数,实质上它也是一个浮点数。如“1”和“1.0”完全是同一个数,执行“1 === 1.0 ”会返回“true”的布尔值。其实在JavaScript语言的底层,根本没有整数(其他编程语言是“int”型)这个类型 。但有的时候JavaScript的值的计算必须要整数才能完成,JavaScript的做法也只是将它64位的数值转换为32位再进行运算而已。
# 数值型的精度问题
浮点数始终是一个不精确的数,在进行浮点数运算的时候会出现一些意料外的情况。比如现在我们拿以下几个浮点数在控制台内进行运算输出,观察结果:
0.1 + 0.2 === 0.3; // false
0.1 + 0.2; // 0.30000000000000004
0.3 / 0.1; // 2.9999999999999996
0.3 - 0.2; // 0.09999999999999998
所以,在进行一些需要高精度的整数计算的时候,最好能对运算数使用保留小数点的方法“toFixed()”,再用对应的十分位数相乘转换回原始计算结果,该方法的括号内需要有一个参数,参数是一个数值型,如toFixed(2)”表示计算结果保留两位小数,然后将得出的结果乘以“100”即会得到一个相对精确的整数。
根据IEEE 754规定,有效数字第一位默认总是1,不保存在64位浮点数之中。在这64位中,第1到第12位表示数值的“指数”,剩余的52位是用二进制表示,也就是说JavaScript的有效数字是一个53位的二进制单位,这意味着JavaScript能精确表示的数值范围在-(253-1)到253-1之间。在JavaScript中对数值求幂(N次方)的函数是“Math.pow(binary,power)”,其中“binary”是指“底数”,函数中的参数意思就是“binary”的“power”次幂(次方),如“Math.pow(5,3)”得出的结果就是125,表示5的3次方。所以,针对上述原理,我们可以算出JavaScript最大能保留的精度数值,写作:Math.pow(2,53)。我们在控制台内做这样一个实验:
从上面示例可以看到,大于2的53次方以后,整数运算的结果开始出现错误。所以,大于等于2的53次方的数值,都无法保持精度。而且大于该值位数的值都不能被保存,被会自动省略掉。
JavaScript还能采用科学计数法来表示一个数值,将数字写在“e”或“E”后方表示指数,如:
123e2 // 12300
-123e2 // -12300
0.0314e2 // 3.14
-0.0314e2 // -3.14
有两种情况,JavaScript会将一个数值自动转换为科学计数法,一种是小数点前多于21位的数字,另外一种是小数点后的“0”多于5位的情况,如:
# NaN
1、含义
在数值型数据中,有一个特殊值 NaN,它表示的是 “非数字”( Not a Number),主要出现在将字符串解析成数字出错的场合。
5 - 'x'; // NaN
上面代码运行时,会自动将字符串x转为数值,但是由于x不是数值,所以最后得到结果为 NaN,表示它是“非数字”(NaN)。
另外,一些数学函数及运算操作的结果会出现 NaN。
需要注意的是,NaN 不是一种独立的数据类型,而是一种特殊数值,它的数据类型依然属于Number,使用 typeof 运算符可以查看数据类型。
typeof NaN; // 'number'
2、运算规则
NaN 不等于任何值,包括它本身。
NaN === NaN; // false
由于数组的 indexOf 方法,内部使用的是严格相等运算符,所以该方法对 NaN 不成立。
xz-----xz[NaN].indexOf(NaN); // -1
NaN 在布尔运算时被当作 false。
Boolean(NaN); // false
NaN 与任何数(包括它自己)的运算,得到的都是 NaN。
NaN + 32; // NaN
NaN - 32; // NaN
NaN * 32; // NaN
NaN / 32; // NaN
3、判断NaN的方法
要判断一个数据的值是否是“NaN”,JavaScript提供了一个方法:isNaN(param),将需要判断的值或变量作为参数放置于该方法内,即可根据返回的值判断该值或变量是否为一个“NaN”,该方法的返回值是一个布尔值,如果返回的值为true,表示值为NaN,如果返回的值为false,那就表示它的值不是NaN,如例:
可以发现,其实直接对值类型进行判断的情况下,除了数值型以外,返回的结果都是true,当然也包括它自身isNaN(NaN),得出的结果同样是true。NaN产生的条件是:当参与算术运算中的某个值不为数值型的值,那返回的结果就会为NaN。但“+”运算符比较特殊,因为有的时候它是可以作为字符串拼接运算符的。观察控制台中输出的结果:
# 数值转换
在实际的数值获取中,很多时候取到的数值并非是以数值存在的,如:字符串"3",像素单位"30px",单个数组元素[30]等,这些类型的值在参与和数值型算术运算时会产生意外的结果,如,一个数值在和一个字符串、像素单位值或单个数组进行“+”运算时,起到的是一个“字符串拼接”的作用,而在执行“-”、“×”或“÷”运算时又是进行基本算术运算。如例:
当然,上面这些“非数值”的内容都还是一个数值内容,能通过程序自动转换的情况。倘若取到的值是一个带单位的(px、em、pt等),情况又有所不同了:
在数值转换中,除了自动转换,还包括下面几个转换方法:
1、parseInt
parseInt 方法的作用是将一个字符串转换为一个数值,它还能将数组首元素转换为一个数值。但这需要考虑实际情况,因为 parseInt 方法转换的原理是单纯地将出现在方法内部前面的内容转换为数值,在进行一些不同单位的数值进行运算时,得出的值不是实际情况中的值,如一个像素单位和一个字符单位的数字进行运算的情况,而且此方法不能转换百分比数。大致如下:
通过上面控制台输出的情况可以发现,parseInt() 方法对内部的数值依次向右转换,一旦遇到“非数字 ”的字符(甚至包含小数点“.”)立即将之前取到的值返回。但如果第一位出现的字符就是“非数字”,那返回的结果就是 NaN,除了为“正负号 ”的情况。
提示:parseInt 转换结果不是 十进制数 就是NaN。
parseInt 方法在转换科学计数法的数字时,会省略e后面的内容:
parseInt("314e-2") // 314
parseInt("21e3") // 21
2、parseFloat
parseFloat 方法用于将一个字符串转为浮点数。在实际开发中,很多时候我们需要的数不仅限于整数,可能还需要更精确的 “浮点数”,这个时候 parseInt() 方法已经是不能满足需求了,那我们需要用到JavaScript对数值进行转换的另外一个全局方法 parseFloat()。该方法的作用是可以将一个字符串或数组首元素转换为一个浮点数,它的大部分特性和 parseInt() 方法一样,但它能对字符串为一个科学计数法的值进行转换。
parseFloat('20.25') // 20.25
parseFloat('[20.25]') // 20.25
parseFloat('20.25e2') // 2025
parseFloat('1234e-2') // 12.34
parseFloat('3.14abcd') // 3.14
parseFloat(' 3.14') // 3.14
parseFloat('A2') // NaN
parseFloat('[]') // NaN
parseFloat(' ') // NaN
3、Number()
提示:参考第一章类型转换。
# 数值进制
作为一门计算机语言,JavaScript自然也支持四种基本的进制表示法,它们分别是:十进制、二进制、八进制和十六进制。
1、十进制
也就是常用是数值,如阿拉伯数字:...-2,-1,0,1,2...(同时也包含每个数字之间的小数)。
2、二进制
需要在数值之前加上前导数:“0b”或“0B”,且前导数后方的数值不能大于1,或是其它内容,否则浏览器会报错,如:
3、八进制
需要在数值之前加上前导数:“0o”或“0O”,且前导数后方的数值不能大于7,或是其它内容,否则浏览器会报错容,如:
4、十六进制
需要在数值之前加上前导数:“0x”或“0X”,且前导数后方的数值不能大于f或F(相当于十进制内的15),或是其它内容,否则浏览器会报错,如:
# 进制转换
通过 parseInt 方法,可以将任意进制数转换为十进制数。但当需要转换的进制数为一个字符串时,该方法只能对十六进制的数进行正确的转换。
parseInt(0b1010) // 10
parseInt(0o12) // 10
parseInt(0xA) // 10
parseInt("0b1010") // 异常 ‘0’
parseInt("0o12") // 异常 ‘0’
parseInt("0xA") // 10
但我们在实际的项目操作中获取到的进制数大多是以字符串的形式存在的,这里我们可以使用 parseInt 的第二个参数,该参数可以告诉该方法需要转换的字符是何种进制的,并将其转换为十进制的数。需要注 意的是,如果配置了第二个参数,那第一个参数就不能包含前导数了,否则除了十六进制会出现异常。
parseInt 方法的第二个参数除了上述几种形式之外还可以为其他进制数,它的范围是2~36之间的数值,只有在这个范围内才会返回有意义的数值,如果超出这个范围则会返回NaN。
parseInt("11", 2) // 二进制 3
parseInt("11", 3) // 三进制 4
parseInt("11", 4) // 四进制 5
parseInt("11", 36) // 三十六进制 37
parseInt("11", 1) // 一进制,异常
parseInt("11", 37) // 三十七进制,异常
上面提到的,是将各种进制数转换为十进制数,有时你可能想将一个十进制数转换为其他进制数,这时我们可以使用 toString() 方法,括号里面的参数为转换到的进制,如下所示:
(10).toString(2) // "1010"
(10).toString(8) // "12"
(10).toString(16) // "a"
# NUMBER OBJECT
1、简介
Number 对象是数值对应的包装对象,可以作为构造函数使用,也可以作为工具函数使用。
// 1、构造函数使用,生成数值对象
var n = new Number(1); // 1
typeof n; // "object"
// 2、工具函数使用,类型转换
Number('3.14'); // 3.14
2、属性
Number.POSITIVE_INFINITY:正的无限,指向Infinity。
Number.NEGATIVE_INFINITY:负的无限,指向-Infinity。
Number.NaN:表示非数值,指向NaN。
Number.MAX_VALUE:表示最大的正数,相应的,最小的负数为-Number.MAX_VALUE。
Number.MIN_VALUE:表示最小的正数(即最接近0的正数,在64位浮点数体系中为5e-324),相应的,最接近0的负数为-Number.MIN_VALUE。
Number.MAX_SAFE_INTEGER:表示能够精确表示的最大整数,即9007199254740991。
Number.MIN_SAFE_INTEGER:表示能够精确表示的最小整数,即-9007199254740991。
3、实例化方法
1)、Number.prototype.toString()
toString() 方法用于将数值转为字符串
var n = 3.14;
n.toString(); // "3.14"
(101).toString(); // "101"
(3.2).toString(); // "3.2"
toString 方法可以接受一个参数,表示输出的进制。如果省略这个参数,默认将数值先转为十进制,再输出字符串;否则,就根据参数指定的进制,将一个数字转化成某个进制的字符串。
(10).toString(2) // "1010"
(10).toString(8) // "12"
(10).toString(16) // "a"
上面代码中,之所以要把10放在括号里,是为了表明10是一个单独的数值,后面的点表示调用对象属性。如果不加括号,这个点会被JavaScript引擎解释成小数点,从而报错。
10.toString(2)
// SyntaxError: Unexpected token ILLEGAL
只要能够让JavaScript引擎不混淆小数点和对象的点运算符,各种写法都能用,一般建议使用括号() 区分。
10..toString() // "10"
10 .toString() // "10"
10.0.toString() // "10"
2)、Number.prototype.toFixed()
toFixed 方法用于将一个数转为指定位数的小数,返回这个小数对应的字符串。
(10).toFixed(2) // "10.00"
(3.1415926).toFixed(2) // "3.14"
(3.1415926).toFixed(3) // "3.141"
3)、Number.prototype.toExponential()
toExponential 方法用于将一个数转为科学计数法形式。该方法的参数表示小数点后有效数字的位数,范围为0到20,超出这个范围,会抛出一个RangeError。
(10).toExponential() // "1e+1"
(10).toExponential(1) // "1.0e+1"
(10).toExponential(2) // "1.00e+1"
(1234).toExponential() // "1.234e+3"
(1234).toExponential(1) // "1.2e+3"
(1234).toExponential(2) // "1.23e+3"
4)、Number.prototype.toPrecision()
toPrecision方法用于将一个数转为指定位数的有效数字。
(12.34).toPrecision(1) // "1e+1"
(12.34).toPrecision(2) // "12"
(12.34).toPrecision(3) // "12.3"
(12.34).toPrecision(4) // "12.34"
(12.34).toPrecision(5) // "12.340"
toPrecision 方法用于四舍五入时不太可靠,跟浮点数不是精确储存有关。
(3.1415926).toPrecision(1) // 3
(3.1415926).toPrecision(2) // 3.1
(3.1415926).toPrecision(3) // 3.14
(3.1415926).toPrecision(4) // 3.141
5)、Number.prototype.isNaN()
isNaN() 方法用来检查一个值是否为 NaN。
4、方法扩展
JavaScript 允许通过原型链 xxx.prototype 为指定对象扩展新的方法。扩展的方法会被对应类型的实例所继承。如下示例中,为 Number 扩展了转换二进制的 toBinary() 方法,该方法可以被所有 Number 实例访问。
Number.prototype.toBinary = function() {
return `0b${this.toString(2)}`;
};
(5).toBinary(); // "0b101"
(1).toBinary(); // "0b1"
(10).toBinary(); // "0b1010"