Skip to content

Number:

  • 常规数字
  • NaN
  • Infinity 正无穷大
  • -Infinity 负无穷大

其他类型值转为数字方法1:Number()

按照计算机底层存储的二进制值进行转换,被用于隐式转换(数学运算,isNaN,==, ...)

字符串转为数字,只要字符串中包含任意一个非有效数字字符(第一个.除外), 结果都是NaN, 空字符串会变为数字0

js
Number('12.5') // 12.5
Number('12.5.4') // NaN
Number('') // 0
Number('12.5px') // NaN
Number('.3') // 0.3

对于数字类型, 不做处理

js
Number(100) // 100
Number(NaN) // NaN

布尔值转为数字

js
Number(true) // 1
Number(false) // 0

null/undefined转为数字

js
Number(null) // 0 (真正的数字0)
Number(undefined) // NaN

Symbol不能转化为数字,会报错

js
Number(Symbol()) // TypeError: Cannot convert a Symbol value to a number

对于BigInt, 丢失n

js
let num = BigInt(10) // 10n
Number(num) // 10
BigInt(-10) // -10n
BigInt(-10n) // -10n
BigInt(-0) // 0n
BigInt(-0n) // 0n
BigInt(+0) // 0n
BigInt(+0n) // 报错
BigInt(0n) // 0n
BigInt(+10) // 10n
BigInt(+10n) // 报错
BigInt(10n) // 10n

引用数据类型转为数字(V8 Webkit底层渲染机制)

  • 1.Symbol.toPrimitive
  • 2.valueOf
  • 3.toString

依次调用上面三个方法, 一旦有一个返回值是基本数据类型, 则不再调用之后的方法,如果这个返回值是 Number 类型,直接使用,如果不是 Number 类型,通过 Number() 转化为 Number 类型再使用,如果调用完三个方法还不能转为基本数据类型, 则报错 Cannot convert object to primitive value

js
const o = { name: 'Tom' }
o.valueOf() // { name: 'Tom' }  -> object
o.toString() // [object Object] -> string

Number({}) // {} => [object Object] => NaN
Number({ name: 'Tom' }) // { name: 'Tom' } => [object Object] => NaN

const o = { 
  name: 'Tom',
  [Symbol.toPrimitive] () {
    return 1
  },
  valueOf () {
    return 2
  },
  toString () {
    return 3
  }
}
Number(o) // 1

数组

js
const arr = [1, 2, 3]
arr.valueOf() // [1, 2, 3] -> object
arr.toString() // '1,2,3' -> string 将数组的每一项转为转为字符串,之间使用,分隔,由于null/undefined没有toString方法,所以直接忽略它们

Number([]) // [] => '' =>  0
Number([12]) // [12] => '12' => 12
Number(['12']) // ['12'] => '12' => 12
Number([12, 13]) // [12, 13] => '12,13' => NaN
Number([12, ]) // [12, ] => '12' => 12
Number([12, null]) // [12, null] => '12,' => NaN
Number([12, undefined]) // [12, undefined] => '12,' => NaN
Number(12, undefined, null]) // [12, undefined, null] => '12,,' => NaN
Number([12, { name: 'Tom'}]) // [12, { name: 'Tom'}] => '12,[object Object]' => NaN

函数

js
function fn () {}
fn.valueOf() // fn () {} -> function
fn.toString() // 'function fn () {}' -> string
Number(fn) // fn () {} => 'function fn () {}' => NaN

其他类型值转为数字方法2: isNaN()

NaN(not a number)不是一个数,但它属于数字类型,NaN 和任何值(包括自己)都不相等

js
NaN !== NaN // true
NaN === NaN // false

isNaN()

  • 检测的值如果是数字类型, 再看是不是 NaN

  • 检测的值如果不是数字类型, 基于 Number() 转化为数字, 再看是不是 NaN

js
isNaN(1) // false
isNaN(NaN) // true
isNaN(Infinity) // false
isNaN(false) // 0 => false
isNaN('AA') // NaN => true
isNaN('12.5') // 12.5 => false
isNaN('12.5px') // NaN => true
isNaN([]) // [] => '' => 0 => false
isNaN([10]) // [10] => '10' => 10 => false
isNaN([10, 20]) // [10, 20] => '10,20' => NaN => true
isNaN({}) // {} => [object Object] => NaN => true
isNaN(null) // 0 => false
isNaN(undefined) // NaN => true
isNaN(Symbol(1)) // 报错

isNaN('hello world') // true
Number.isNaN('hello world') // false,必须是 NaN
Number.isNaN(NaN) // true

其他类型值转为数字方法3: parseInt() / parseFloat()

parseInt([val], [进制])

parseFloat([val])

val 必须是字符串, 如果是非字符串. 先转化为字符串。转化为字符串的规则: 从左到右依次查找有效数字字符,直到遇见非有效数字字符则停止查找(parseInt把所有.当作非有效数字字符, parseFloat把遇到的第一个 . 当作有效数字字符,其它 . 都是非有效数字字符),不管后面是否还有数字字符, 都不找了, 再把当前找到的内容转为数字。

radix 不设置(或写 0 ), 按 10 进制处理, 如果字符串以 '0x' 开始,默认 16 进制处理...,radix范围限定在 2 - 36, 不在这个范围都是 NaN ( 0 除外),先在 val 中,从左往右找到所有符号 radix 进制的内容,直到遇到不符合的内容则不再查找,无论是否后面是否还有符合内容,然后将找到的内容看作 [radix] 进制,转为十进制,val为 '' 时,结果为 NaN

js
parseInt('12px') <=> parseInt('12px', 10)
// 在'12px'字符串中找到所有符合10进制的内容(0-9)  => '12' => '12'当作10进制转换为10进制 => 12

parseInt('12px', 2)
// 在’12px‘字符串中找到所有符合2进制的内容(0-1)  => '1' => '1'当作2进制转换为10进制 => 1 * 2 ^ 0 = 1
js
parseInt(27.2, 0) // '27.2' => '27' => 27
parseInt(0, 1) // NaN
parseInt('0013', 2) '001' => 1 * 2 ^ 0 = 1
parseInt('14px', 3) '1' => 1 * 3 ^ 0 = 1
parseInt(123, 4) '123' => 3 * 4 ^ 0 + 2 * 4 ^ 1 + 1 * 4 ^ 2 = 27
parseInt(072) // 58 072 => 58 => '58' JS中,一个0开始的数字(不是字符串),并且每一位都 < 8, 浏览器会默认把它当作八进制 2 * 8 ^ 0 + 7 * 8 ^ 1 = 58
parseInt(072, 2) // NaN 072 => 58 => '58'
parseInt(078) // 78 078 => '078' 虽然以0开始,但是有数字超过7,不会被当作八进制
parseInt('072') // 72
parseInt(0xf) // // 15 0xf => 15 => '15'
parseInt(0xf, 2) // 1 0xf => 15 => '15'
parseInt('00d72') // 0
parseInt('12.5px') // 12
parseInt('0.8') // 0
parseInt('12px14') // 12
parseInt('width: 12.5px') // NaN
parseInt(true) // 'true' => NaN
parseInt(null) // 'null' => NaN
parseInt(NaN) // 'NaN' => NaN
parseInt('') // NaN
parseInt([]) // => '' => NaN
parseInt([12]) // => '12' => 12

parseFloat(27.2, 99)) => 27.2 // parseFloat不支持进制处理,没有第二个参数
parseFloat('12.5px') // 12.5
parseFloat('.123px') // 0.123
parseFloat('..123') // NaN
parseFloat('1') // 1
parseFlost('') // NaN

其他类型值转为数字方法4: ==

js
null == 0 // false, null在这里的隐式转换有问题
Number(null) == 0 // true

其他类型值转为数字方法5: 数学运算,调用Number()

js
+'5' // 5
+'12px' // NaN
+'12.5' // 12.5
+null === 0 // true

'12.5' * 1 === 12.5 // true
null * 1 === 0 // true

其他

toFixed 保留小数点后面N位,最后结果是一个字符串

js
(3.1415926).toFixed(2) // '3.14'

最大安全数(长度为 16),JS 能够有效识别的最大整数

js
Number.MAX_SAFE_INTEGER // 9007199254740991
Math.pow(2, 53) - 1 // 9007199254740991
9007199254740992 === 9007199254740993 // true

BigInt 管理超过安全数值的数字,会在数字后面加个 n, BigInt 只能和 BigInt 相加, 数字后面有 n 就是 BigInt 类型

js
// 字面量:
9007199254740992n + 1n // 9007199254740993n
js
// 构造函数: 
BigInt(9007199254740992) + BigInt(1) // 9007199254740993n

浮点数精度问题

JS 采用的是 IEEE 754 双精度浮点数,使用64位二进制来表示一个数字

第1位:符号位,0表示正数 1表示负数 S

第2到12位 11位指数,存储指数部分 E

第13到64位 52位尾数,存储小数部分(即有效数字) F

尾数部分在规约形式下第一位默认为1(省略不写),即“隐藏位”或“隐含1”,不存入实际的52位里,而是自动补上。也就是说,实际存储的52位只存小数点后的部分。例如,实际的有效数字是1.xxx...(1后面跟52位),而不是0.xxx...,非规约形式下(即指数全为0时),隐藏位不是1,而是0,此时尾数就是0.xxx...。

十进制转二进制: [Number].toString([radix]),radix默认为10

js
let n = 4
n.toString(2) // '100' 二进制字符串

let n = 0.1
n.toString(0.1) // '0.0001100110011001100110011001100110011001100110011001101'

0.1 + 0.2 = 0.30000000000000004

将十进制小数转为二进制时,需要不断乘以 2,取整数部分作为二进制位,余数继续乘以 2,直至余数为 0。但是,像 0.1、0.2 这样的十进制小数,用二进制表示时会变成无限循环小数,无法精确表示。

js
// 以十进制小数 0.625 为例,将它转换为二进制。

0.625 × 2 = 1.25
// 取整数部分 1,这是二进制小数的第一位。
// 余数是 0.25。

0.25 × 2 = 0.5
// 取整数部分 0,这是二进制小数的第二位。
// 余数是 0.5。

0.5 × 2 = 1.0
// 取整数部分 1,这是二进制小数的第三位。
// 余数是 0.0,运算结束。

// 结果
// 把上面得到的整数部分按顺序排列,0.625 的二进制小数部分就是:
0.101

计算机只能存储有限位数(JavaScript 中是 64 位),超出的部分会被截断或用舍入规则处理。 这就导致实际存储的二进制值和我们期望的十进制值之间有微小误差。

当 0.1 + 0.2 时,实际上是两个已经有微小误差的近似值相加,最终结果也带有误差,所以出现 0.30000000000000004。

js
(0.1 * 10 + 0.2 * 10) / 10

function equal (a, b) {
  return Math.abs(a - b) < Number.EPSILON // Number.EPSILON表示1与大于 1 的最小的浮点数之间的差值
}