HakurouKen 的博客

Javascript 中的字符串转数字

在 javascript 中,我们经常会进行一些类型转化,尤其是字符串转化为数字。由于 javascript 的灵活性,我们有很多的方法能够完成这一目的,但是对于一些 edge cases,不同的方式转化经常会得到不同的效果。被一些特殊情况坑了几次之后,这里专门对这个问题咬文嚼字一下。

茴字的四种写法

利用位运算隐式转换

在很多介绍 javascript 的奇技淫巧的文章/书籍中,都会提到这种转化,例如:

1
2
3
~~'1000' === 1000
'999' >> 0 === 999
998.1 | 0 === 998

这种办法的优点是省字符,但是它也有一些弊端:

  1. 参与位运算的数字都需要在 int32 的范围内,否则会溢出
  2. NaNInfinity 会被当作 0 处理

Number

javascript 中内置了转换数字方法 Number

1
2
3
Number('1000') === 1000
Number('1e3') === 1000
Number('-1e-3') === 0.001

我们也可以使用四则运算来进行隐式转换,这样可以节省一些字符:

1
2
+'1000' === 1000
'999' * 1 === 999

Math 相关方法

1
2
3
4
Math.floor('1000.5') === 1000
Math.ceil('999.1') === 1000
Math.round('998.3') === 998
Math.floor('-999.9') === 1000

这些方法的本质都是先调用 Number 隐式转换为数字,然后再执行对应的方法。不过需要额外注意的是,Math 中的这几个取整的方法和数学中的“取整”概念是一致的,在处理负数时需要额外注意。

parseInt

MDN 中对 parseInt 的说明很言简意赅,就是字符串转整数的方法:

1
2
parseInt('994', 10) === 994
parseInt('980px', 10) === 980

需要注意的是,parseInt 在转换的时候,会先将第一个参数转化为 String 类型,然后再转化为字符串。这在一些输入类型不确定的场景下,会导致一些奇怪的问题,例如:

1
2
parseInt(3e-6, 10) === 0
parseInt(3e-7, 10) === 3

出现这个问题的原因是因为 3e-7 超出了精度范围,因此会使用科学计数法表示,而 3e-6 则不会。因此上面的等式实际的执行过程是:

1
2
parseInt(3e-6, 10) === parseInt('0.000003', 10) === 0
parseInt(3e-7, 10) === parseInt('3e-7', 10) === 3

总结

  1. 因为有溢出的风险,所以不建议使用位运算的隐式转换(eslint 的 no-bitwise 规则)。
  2. 如果你使用了 typescript 或者 flow 等保证了输入类型,则永远优先使用 parseInt(或 parseFloat)。它不仅语义最清晰,而且性能最好。
  3. 如果是你的目的是对数字输入和字符串输入进行兼容,尽量使用 Number 转换。考虑到绝大多数的类型转换,都是用于比较是否相等(例如兼容数字 ID 和字符串 ID),用 Number 已经足够。如果后续需要用于计算,再按照需要,使用 Math.floor 或者 parseInt 等进行转化。