JavaScript中的十进制数字验证:为什么IsNumeric()会破坏代码以及如何修复
\n\n每个至少写过一周JavaScript的开发者,迟早都会遇到这样一个任务:“检查一个字符串是否为数字”。看起来,这能有多难?内置函数 isNaN() 或 parseFloat() 就能轻松搞定。但事实并非如此。Stack Overflow上的问题 “Validate decimal numbers in JavaScript — IsNumeric()” 获得了数十万次浏览和几十个相互矛盾的答案。为什么?因为JavaScript是一种 typeof NaN === 'number' 且空字符串被视为零的语言。
在本文中,我们不仅会给出一个现成的 isNumeric() 函数。我们还将分析 5个最常见且危险的错误,即使是经验丰富的程序员也常会犯,并展示如何避免它们。您将了解到为什么 isNaN 是一个陷阱,类型转换是如何工作的,以及哪种JS数字验证方法真正适用于生产环境。
为什么isNaN()是邪恶的(以及它何时有效)
\n\n让我们从显而易见的地方开始。许多新手会这样写:
\n\nfunction isNumeric(n) {\n return !isNaN(n);\n}\n\n\n乍一看很合理:如果不是NaN,那就是数字。但让我们测试一下:
\n\nconsole.log(isNumeric('123')); // true — 正常\nconsole.log(isNumeric('12.3')); // true — 正常\nconsole.log(isNumeric('')); // true — 哎呀!空字符串是数字?\nconsole.log(isNumeric(' ')); // true — 空格也是数字?\nconsole.log(isNumeric(null)); // true — null是数字?\nconsole.log(isNumeric(true)); // true — true是1\n\n\n错误 #1: isNaN() 首先将参数转换为数字,然后进行检查。空字符串 '' 被转换为 0,null 被转换为 0,true 被转换为 1。结果,该函数将不是数字的东西当成了数字。
有一个严格版本——Number.isNaN() (ES6),但它只检查值是否为NaN,而不进行类型转换。它也不适用于数字验证。
parseFloat:英雄还是恶棍?
\n\n第二种流行的方法是使用 parseFloat():
function isNumeric(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n}\n\n\n这个版本好一些,但它有另一个问题:
\n\nconsole.log(isNumeric('123abc')); // true — parseFloat会截取'123',忽略'abc'\nconsole.log(isNumeric('12.3.4')); // true — parseFloat根本不会注意到第二个点\nconsole.log(isNumeric('Infinity')); // true — 无穷大是数字?\n\n\n错误 #2: parseFloat() 解析字符串直到第一个无效字符。字符串 '123abc' 对它来说是 123。但用户可能不小心按了多余的键,我们必须捕获这种情况。
错误 #3: isFinite() 排除了 Infinity 和 -Infinity,但放过了 NaN 和 undefined。问题在于 isFinite(null) 返回 true,因为 null 被转换为 0。
错误 #4:正则表达式——万能药还是新的地狱?
\n\n当开发者意识到内置函数不可靠时,他们常常会走向另一个极端——正则表达式。这时真正的混乱就开始了。
\n\n这是一个典型但 危险 的正则表达式:
\n\nfunction isNumeric(n) {\n return /^\\d+$/.test(n);\n}\n\n\n问题:
\n- \n
- 不允许小数点(
'12.3'— false) \n - 不允许负数(
'-5'— false) \n - 不允许科学计数法(
'1e5'— false) \n
这是一个“完