某天写需求做数字格式化,需要判断接口返回的字段是否是数字,如果不是则显示--,第一反应是使用isNaN()判断,结果我大意了……
isNaN()
以下的参数都不是数字,然而使用isNaN判断却发现了大宝贝,只有少数几种类型符合期望,到底是哪里出了问题呢?
isNaN(null)
// output: false
isNaN(undefined)
// output: true
isNaN('')
// output: false
isNaN('1')
// output: false
isNaN('a')
// output: true
isNaN(true)
// output: false
isNaN([])
// output: false
isNaN([1])
// output: false
isNaN([1, 2])
// output: true
isNaN({})
// output: true
isNaN({a: 123})
// output: true
首先查看MDN上的定义:
The
isNaN()function determines whether a value isNaNor not. Because coercion inside theisNaNfunction can be surprising, you may alternatively want to useNumber.isNaN().
文档表明了isNaN是存在一些不可预知的情况,再去查看tc39关于isNaN的描述,发现处理流程是这样的:
- 根据下面表格的规则,将参数转换成Number
| 参数类型 | 转换结果 |
|---|---|
| Undefined | NaN |
| Null | 0 |
| Boolean | true返回1, false返回0 |
| Number | 不转换 |
| String | 转换为数字,如果报错,则返回NaN |
| Symbol | 报错 |
| BigInt | 报错 |
| Object | 首先判断这个参数的对象类型的preferredType转换为对应的原始数据类型,然后再根据上面对应的类型进行转换 |
ps1: []、[1]、[1, 2]这三种情况的结果不一样,是因为它们先根据Object类型转化为字符串''、'1'、'1, 2',然后再根据String类型的规则转换为数字0、1、NaN,因此最后的结果是false、false、true
ps2: preferredType为Number的常见对象包括Date等
- 如果得到的转换结果是NaN,则返回true,否则返回false
Number.isNaN()
按照MDN的提示,想起还有一个Number.isNaN()的方法可以一试,在tc39关于Number.isNaN中,简而言之是判断是否是数字类型且是NaN,是则返回true,否则返回false。
结论
综上所述,不考虑Symbol和BigInt的情况下,基本上isNaN(val)等价于Number.isNaN(Number(val))
回到一开始的场景,我的解决方案如下:
typeof val === 'number' && !Number.isNaN(val) ? val : '--'