数值处理
一、StringToNumber
字符到数字的类型转换,存在一个语法结构,支持十进制、二进制、八进制和十六进制比如:
+'30' // 30
+'ob111' // 7
+'0o13' // 11
+'0xFF' // 255
此外,JavaScript支持的字符串语法还包括正负号科学计数法,可以使用大写或者小写的e来表示:
+'1e3' // 1000
+'-1e-2' // -0.01
也可以使用函数Number来显式转换
Number('30') // 30
Number('0b111') // 7
Number('0o13') // 11
Number('0xFF') // 255
Number('1e3') // 1000
Number('-1e-2') // -0.01
特别要注意,表达式所产生的隐式调用并不引用window.Number属性来调用对应的程序,我会在以下(二)中详细解释这个问题。
需要注意的是,parseInt 和 parseFloat 并不使用这个转换,所以支持的语法跟这里不尽相同。这个问题在以下(三)中讨论
二、小花样:window.Number函数与内置Number
先来看一个例子:
以上例子说明window的Number属性是可覆写的(writable=true),下面对Number函数进行覆写(实质上是修改Number的指向):
覆写之后window.Number失去原有的特性:
(注意第二句new Number(123) 时自定义的return语句会被自动添加return this屏蔽)。但是覆写window.Number后我们还是可以使用以下隐式类型转换
这说明了:
- 覆写之前Number所指向的程序(记为X)并没有释放,而是继续存在,只修改了window.Number的指向后,无法再用Number属性找到X。但是,隐式类型转换使用的依旧是X,这说明底层c++保留有指向X的指针
- 隐式类型转换并没有调用Number()(否则这里应该就输出“覆盖后的Number”了), 准确地说,没有通过window的Number属性变量来对X寻址,而是通过内部的一个变量(假设记为innerNumber,C++不一定使用这个名字)来对X寻址。
数字类型的装箱操作也是使用innerNumber来寻址X,而非使用window.Number。利用装箱原理,我们可以找回X
这样,window.Number和innerNumber就都指向X了。
三、parseInt 和 parseFloat
在不传入第二个参数的情况下,parseInt只支持16进制前缀“0x”,而且会忽略非数字字符,也不支持科学计数法。
在一些古老的浏览器环境中,parseInt还支持0开头的数字作为8进制前缀,这是很多错误的来源。所以在任何环境下,都建议传入parseInt的第二个参数,而parseFloat则直接把原字符串作为十进制来解析,它不会引入任何的其他进制。
多数情况下,Number 是比 parseInt 和 parseFloat 更好的选择。
1. parseInt
parseInt
函数将其第一个参数转换为字符串,解析它,并返回一个整数或NaN。如果不是NaN,返回的值将是作为指定基数(基数)中的数字的第一个参数的整数。
例如:radix
参数为10 将会把第一个参数看作是一个数的十进制表示,8 对应八进制,16 对应十六进制,等等。基数大于 10
时,用字母表中的字母来表示大于9
的数字。例如十六进制中,使用 A
到 F
。
如果parseInt
的字符不是指定基数中的数字,则忽略该字符和所有后续字符,并返回解析到该点的整数值。parseInt
将数字截断为整数值。允许使用前导空格和尾随空格。
一些数中可能包含e字符(例如6.022e23),使用parseInt去截取包含e字符数值部分会造成难以预料的结果。例如:
parseInt("6.022e23", 10); // 返回 6
parseInt(6.022e2, 10); // 返回 602
parseInt不应该用作 Math.floor()
的替代品。
如果 parseInt
遇到了不属于radix
参数所指定的基数中的字符那么该字符和其后的字符都将被忽略。接着返回已经解析的整数部分。parseInt
将截取整数部分。开头和结尾的空白符允许存在,会被忽略。
在基数为 undefined
,或者基数为 0 或者没有指定的情况下,JavaScript 作如下处理:
- 如果字符串
string
以"0x"或者"0X"开头, 则基数是16 (16进制). - 如果字符串
string
以"0"开头, 基数是8(八进制)或者10(十进制),那么具体是哪个基数由实现环境决定。ECMAScript 5 规定使用10,但是并不是所有的浏览器都遵循这个规定。因此,永远都要明确给出radix参数的值。 - 如果字符串
string
以其它任何值开头,则基数是10 (十进制)。
如果第一个字符不能被转换成数字,parseInt
返回NaN
。
算术上, NaN
不是任何一个进制下的数。 你可以调用isNaN
来判断 parseInt
是否返回 NaN
。NaN
参与的数学运算其结果总是 NaN
。
将整型数值以特定基数转换成它的字符串值可以使用 intValue.toString(radix)
.
更多细节可在MDN查看:
2. parseFloor
四、numberToString
在较小的范围内,数字到字符串的转换是完全符合你直觉的十进制表示。当Number绝对值较大或者较小时,字符串表示则是使用科学计数法表示的。这个算法细节繁多,我们从感性的角度认识,它其实就是保证了产生的字符串不会过长。
具体的算法,你可以去参考JavaScript的语言标准。由于这个部分内容,我觉得在日常开发中很少用到,所以这里我就不去详细地讲解了。
总结
对于window的7种基本数据类型对应的构造函数,如果这些构造可覆写,则隐式类型转换和装箱操作都不会通过window对象里对应的属性(如Number)来寻址目标程序块X,而是通过我们普通方法访问不到的变量来寻址目标程序块X。
另外,我们可以利用装箱原理来访问程序块X。
补充
箭头函数不能用作构造函数!!!