前言
最近在做题目LeetCode表示数值的字符串这道题的时候
对数值类型又有了新的认识 打算重新总结下
1. 整数、浮点数
JavaScript 内部,所有数字都是以64位浮点数形式储存,即使整数也是如此。所以,1与1.0是相同的,是同一个数
1 === 1.0
// true
保留两位小数
1 四舍五入
1 | var num = 2.446242342 |
2 | num = num.toFixed(2) // 输出是字符串 需要用parseFloat或Number再转一下 |
2 不四舍五入
1 | Math.floor(15.7784514000 * 100) / 100 // 15.77 |
2. 数值的进制
十进制:没有前导0的数值
八进制:有前缀0o或0O的数值,或者有前导0、且只用到0-7的八个阿拉伯数字的数值
012 // 10 0o12 // 10 0O12 // 10 078 // 78 超出范围 无效的八进制 解析为十进制
十六进制:有前缀0x或0X的数值
0xA // 10 0x12 // 18 0XAB //171
二进制:有前缀0b或0B的数值
0b111 // 7 0B11 // 3
默认情况下,JavaScript 内部会自动将八进制、十六进制、二进制转为十进制
3. 进制之间的转换
1) Number.prototype.toString([radix])
toString() 方法返回指定 Number 对象的字符串表示形式。
radix 指定要用于数字到字符串的转换的基数(从2到36)。如果未指定 radix 参数,则默认值为 10。
1 | (12).toString() // "12" 十进制转十进制 |
2 | (012).toString() // "10" 八进制转十进制 |
3 | (10).toString(2) // "1010" 十进制转二进制 |
4 | (0b1010).toString(8) // "12" 二进制转八进制 |
toString()以后是字符串格式 在通过格式转换 转为数值类型就好啦 怎么转换,后续有介绍哦
2) parseInt(string,[radix])
parseInt() 可以将任意进制的数转为10进制
1 | parseInt(111,2) // 3 |
2 | parseInt(12,8) // 10 |
3 | parseInt(0xA) // 10 |
4 | parseInt(014) // 12 |
关于parseInt()的详细解释在后面
4. Number() 函数
在说NaN 之前先说下Number函数
Number() 函数是数值类型的构造函数 同时也可以将其他类型转为数值类型
如果无法转为数值 就返回NaN
Number()函数的转换规则如下
Boolean false => 0 true => 1
Number 直接返回
null => 0
undefined => NaN
字符串
1 空字符串转为0
2 只包含数字(包含前导正负号) 将其转为十进制
3 如果包含有效的进制格式 将转为相同大小的十进制
4 如果包含有效的浮点格式 将其转为对应的浮点数值
5 如果不符合以上条件 返回NaN对象 调用对用的valueOf()方法 然后按照前面的规则进行转换 如果转换的结果是NaN 则调用对象的toString()方法,然后再依照前面的规则进行转换
Number(false) // 0 布尔值false对应0 Number(123) // 123 数值类型直接返回 Number(null) // 0 Number(undefined) // NaN Number('') // 0 Number('abc') // NaN 'abc' 不能被转为数字 Number('123') // 123 '123' 可以被转为数字 Number('123a) // NaN '123a' 不是只包含数字不能被转换 Number('0x12') // 18 0x12是有效的进制格式 站位10进制 Number('1.1') // 1.1 有效的浮点格式 转为相应的浮点格式 Number([]) // 0 因为[].toString() ==> '' Number('') ==> 0 Number([2]) // 2 因为[2].toString() ==> '2' Number('2') ==> 2 Number([1,2,3]) // NaN [1,2,3].toString() ==> '1,2,3' 不能转为数值 Number({}) // NaN ({}).toString() ==> "[object Object]" 不能转为数值
所以对于leetCode 这道题我们可以利用Number()函数对于字符串的转换规则 如果结果是NaN 就不符合条件 否则就是可以转为数值的字符串
1 | var isNumber = function (s) { |
2 | s = s.trim() |
3 | if (s == '') return false // 因为 Number('') === 0 所以需要做特殊判断 |
4 | return !isNaN(Number(s)) |
5 | } |
5. NaN
NaN 表示“非数字”(Not a Number) 是一个特殊的数值 数据类型依然属于Number
- 主要出现在将字符串解析成数字出错的场合
- 一些数学函数的运算结果会出现NaN 比如 0除以0也会得到NaN
- NaN不等于任何值,包括它本身
针对NaN的特性 ES定义了isNaN()函数 该函数接受一个参数 可以为任何类型 isNaN()会尝试将这个参数转为数值 转换规则与Number()函数一致 所以isNaN方法可以用来判断一个值是否 “不是数值”
NaN === NaN // false
isNaN(NaN) // true
isNaN(123) // false
// isNaN()只对数值有效,如果传入其他值,会先转成数值类型
isNaN('') // false
isNaN('hello') // true
//相当于
isNaN(Number('hello')) //true
isNaN(false) // false false会转为0
isNaN({}) //true
isNaN([1,2]) //true
// 所以isNaN() 为true的时候,有可能是一个字符串,或对象或数组
// 所以使用isNaN()时,需要先判断下数据类型
// 但是当数组为空,或数组长度为1时返回false
isNaN([]) //false
isNaN([1]) //false
由于isNaN()函数会对参数进行转换 那这题又可以直接写为
1 | var isNumber = function (s) { |
2 | s = s.trim() |
3 | if (s == '') return false // 因为 Number('') === 0 所以需要做特殊判断 |
4 | return !isNaN(s) |
5 | } |
6. parseInt()
parseInt()解析一个字符串并返回指定基数的十进制整数, radix 是2-36之间的整数,表示被解析字符串的基数。 如果不能解析的话返回NaN
parseInt 参数
parseInt(string,[radix]) 接受两个参数
- string 如果参数不是字符串类型 会通过toString()方法转为字符串
- radix 从2-36 表示字符串的基数 需要注意的是 10不是默认值
如果 radix 是 undefined、0或未指定的,JavaScript会假定以下情况:
- 如果输入的 string以 “0x”或 “0x”(一个0,后面是小写或大写的X)开头,那么radix被假定为16,字符串的其余部分被当做十六进制数去解析。
- 如果输入的 string以 “0”(0)开头, radix被假定为8(八进制)或10(十进制)。具体选择哪一个radix取决于实现。ECMAScript 5 澄清了应该使用 10 (十进制),但不是所有的浏览器都支持。因此,在使用 parseInt 时,一定要指定一个 radix。
- 如果输入的 string 以任何其他值开头, radix 是 10 (十进制)。
1 | parseInt(110,2) // 6 二进制110 转为十进制是 6 |
2 | parseInt(123,8) // 83 八进制的123 转为十进制是83 |
3 | parseInt(89,8) // NaN 不是合法的八进制 |
4 | parseInt('014') // 14 被当做十进制解析 |
5 | parseInt(014) // 12 因为(014).toString() => 12 |
一道有意思的面试题
1 | ["1", "2", "3"].map(parseInt) // [1,NaN,NaN] |
2 | // 相当于parseInt(1,0) => 1 string不以任何其他值开头 radix为十进制 parseInt(1,10) => 1 |
3 | // parseInt(2,1) => NaN 1进制不合法 所以返回NaN |
4 | // parseInt(3,2) => NaN 3 是不合法的二进制 所以返回NaN |
parseInt 忽略后续字符
如果 parseInt 遇到的字符不是指定 radix 参数中的数字,它将忽略该字符以及所有后续字符,并返回到该点为止已解析的整数值。 parseInt 将数字截断为整数值。 允许前导和尾随空格。
1 | parseInt('123a',2) // 1 因为符合二进制的只有1 |
2 | parseInt('1123110',2) // 3 符合二进制的只有前面的11 找到2不符合 会忽略后面的字符 解析为十进制为3 |
3 | parseInt('123abc') // 123 这通常用来在字符串中提取前面的数值 |
7. parseFloat()
parseFloat() 函数解析一个参数(必要时先转换为字符串)并返回一个浮点数。
- 如果 parseFloat 在解析过程中遇到了正号(+)、负号(- U+002D HYPHEN-MINUS)、数字(0-9)、小数点(.)、或者科学记数法中的指数(e 或 E)以外的字符,则它会忽略该字符以及之后的所有字符,返回当前已经解析到的浮点数。
- 第二个小数点的出现也会使解析停止(在这之前的字符都会被解析)。
- 参数首位和末位的空白符会被忽略。
- 如果参数字符串的第一个字符不能被解析成为数字,则 parseFloat 返回 NaN。
- parseFloat 也可以解析并返回 Infinity。
- parseFloat解析 BigInt 为 Numbers, 丢失精度。因为末位 n 字符被丢弃。
1 | // 以下的栗子全部返回3.14 |
2 | parseFloat(3.14); |
3 | parseFloat('3.14'); |
4 | parseFloat(' 3.14 '); |
5 | parseFloat('314e-2'); |
6 | parseFloat('0.0314E+2'); |
7 | parseFloat('3.14some non-digit characters'); |
8 | parseFloat({ toString: function() { return "3.14" } }); |
8. 其他类型转换为数值类型
有三个函数可以将非数值转为数值类型: Number() parseInt() parseFloat()
这三个函数对于同样的输入会返回不同的类型
方法1 Number()
转换规则上面已说过
Number('1') // 1
Number('1.123') // 1.123
Number('1.abc') // NaN
Number('') // 0
Number(true) // 1
Number(false) // 0
Number(null) // 0
Number(undefined) // NaN
Number({a:1}) // NaN
方法2 parseInt()
parseInt()
主要是将字符串转为整数
它会从第一个字符开始,直到不是数字结束
parseInt('1') //1
parseInt('1.23') // 1
parseInt(' 123') // 123
parseInt('1234abc') // 1234
parseInt('abc') // NaN
parseInt('a567bc') // NaN
parseInt(true) // NaN
parseInt(null) // NaN
parseInt(undefined) // NaN
parseInt({a:1}) // NaN
parseInt([1,2,3]) // 1
方法3 parseFloat()
parseFloat() 主要是将字符串转为浮点数
parseFloat('3.14') // 3.14
parseFloat('314e-2') // 3.14
parseFloat('0.0314E+2') // 3.14
// 其他与parseInt()结果相同
方法4 - 0
'1' - 0 // 1
'1.23' - 0 // 1.23
'1.abc' -0 // NaN
'' - 0 // 0
true -0 // 1
false - 0 // 0
null - 0 // 0
undefined - 0 // NaN
({a:1}) - 0
方法5 取正
+ '1' // 1
+ '1.23' // 1.23
+ '1.abc' // NaN
+ '' // 0
+ true // 1
+ false // 0
+ null // 0
+ undefined // NaN
+ {a:1} // NaN
9. 补充一个比较运算符的坑
不同类型
比较不同类型的数据也许会出现不可预料的结果。
如果将字符串与数字进行比较,那么在做比较时 JavaScript 会把字符串转换为数值。
空字符串将被转换为 0 非数值字符串将被转换为始终为 false 的 NaN。
1 | ' ' >= 0 && ' ' <= 9 // true 因为空字符串会转为0 |
2 | 'a' >= 0 || 'a' <= 9 // false 'a' 转为数值 NaN |
3 | '123a' >= '123' // false '123a'也会转为NaN |
相同类型都为字符串
字符串大小会按照字母排序
1 | '2' > '12' // true |
2 | '2020-6-8' > '2020-6-14' // true 因为 '8' > '14' |
3 | // 所以时间格式比较日期格式要yyyy-MM-dd去比较 |
4 | '2020-06-08' > '2020-06-14' // false '08' < '14' |