没想到吧之isNaN

339 阅读2分钟

某天写需求做数字格式化,需要判断接口返回的字段是否是数字,如果不是则显示--,第一反应是使用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 is NaN or not. Because coercion inside the isNaN function can be surprising, you may alternatively want to use Number.isNaN().

文档表明了isNaN是存在一些不可预知的情况,再去查看tc39关于isNaN的描述,发现处理流程是这样的:

  1. 根据下面表格的规则,将参数转换成Number
参数类型转换结果
UndefinedNaN
Null0
Booleantrue返回1, false返回0
Number不转换
String转换为数字,如果报错,则返回NaN
Symbol报错
BigInt报错
Object首先判断这个参数的对象类型的preferredType转换为对应的原始数据类型,然后再根据上面对应的类型进行转换

ps1: [][1][1, 2]这三种情况的结果不一样,是因为它们先根据Object类型转化为字符串'''1''1, 2',然后再根据String类型的规则转换为数字01NaN,因此最后的结果是falsefalsetrue

ps2: preferredType为Number的常见对象包括Date

  1. 如果得到的转换结果是NaN,则返回true,否则返回false

Number.isNaN()

按照MDN的提示,想起还有一个Number.isNaN()的方法可以一试,在tc39关于Number.isNaN中,简而言之是判断是否是数字类型且是NaN,是则返回true,否则返回false

结论

综上所述,不考虑SymbolBigInt的情况下,基本上isNaN(val)等价于Number.isNaN(Number(val))

回到一开始的场景,我的解决方案如下:

typeof val === 'number' && !Number.isNaN(val) ? val : '--'