在JavaScript中验证十进制数 - IsNumeric()
在JavaScript中验证十进制数 - IsNumeric()
如何在JavaScript中验证十进制数字的最干净、最有效的方法?
额外加分项:
- 清晰易懂。解决方案应干净简单。
- 跨平台。
测试用例:
01. IsNumeric('-1') => true 02. IsNumeric('-1.5') => true 03. IsNumeric('0') => true 04. IsNumeric('0.42') => true 05. IsNumeric('.42') => true 06. IsNumeric('99,999') => false 07. IsNumeric('0x89f') => false 08. IsNumeric('#abcdef') => false 09. IsNumeric('1.2.3') => false 10. IsNumeric('') => false 11. IsNumeric('blah') => false
啊啊啊!不要听正则表达式的答案。使用正则表达式处理这个问题非常棘手,我说的不仅是性能问题。使用正则表达式非常容易出现微妙的错误,而这些错误又很难被发现。
如果你不能使用 isNaN()
,那么下面的方法会更加好用:
function IsNumeric(input) { return (input - 0) == input && (''+input).trim().length > 0; }
下面是它的工作原理:
(input - 0)
表达式强制 JavaScript 将你的输入值进行类型转换,为了进行减法操作,输入值必须首先被解释为一个数字。如果转换为数字失败,该表达式的结果将是 NaN
。这个数值结果随后会与你传入的原始值进行比较。由于左侧现在是数字的,因此再次使用了类型转换。现在,输入来自两侧都是从同一原始值转换为相同类型的,您可能认为它们应该总是相同的(始终为真)。然而,有一条特殊规则,规定 NaN
永远不等于 NaN
,因此一个无法转换为数字的值(仅限无法转换为数字的值)将返回 false。
长度检查是针对空字符串的特殊情况。还要注意的是,它在你的 0x89f 测试中未通过,但这是因为在许多环境中,这种方式定义数字文字是可以接受的。如果您想捕捉该特定情况,您可以添加一个额外的检查。更好的方法是,如果您不使用 isNaN()
的原因是这个,那么只需在 isNaN()
周围封装自己的函数,即可额外执行这个检查。
总之,如果想知道一个值是否可以转换为数字,那就真的试着将其转换为数字。
我回去做了一些研究,为什么一个空格字符串没有预期的输出,现在我想我明白了:一个空字符串被强制转换为0
而不是NaN
。只需要在长度检查之前修剪字符串即可处理这种情况。
对新代码运行单元测试,只失败于无穷大和布尔文字,而唯一可能出现问题的时候是如果你生成代码(真的,谁会输入一个文字并检查它是否为数值?你应该知道),这将是一些奇怪的生成代码。
但是,再次提醒,唯一的原因就是如果你必须避免isNaN()。
@Joel's answer很接近,但在以下情况下会失败:
// Whitespace strings: IsNumeric(' ') == true; IsNumeric('\t\t') == true; IsNumeric('\n\r') == true; // Number literals: IsNumeric(-1) == false; IsNumeric(0) == false; IsNumeric(1.1) == false; IsNumeric(8e5) == false;
不久前,我不得不实现一个IsNumeric
函数,以查找变量是否包含数字值,不考虑其类型,它可以是包含数字值(我还必须考虑指数表示法等)的String
,Number
对象,任何东西都可以传递给该函数,我不能做出任何类型假设,要注意类型转换(例如+true == 1;
但true
不应被视为"numeric"
)。
我认为值得分享这组+30个单元测试,以验证各种函数实现,也分享通过所有测试的一个函数:
function isNumeric(n) { return !isNaN(parseFloat(n)) && isFinite(n); }
附言: isNaN和isFinite由于强制转换为数字而具有混淆的行为。在ES6中,Number.isNaN和Number.isFinite会修复这些问题。在使用它们时请牢记这一点。
isNumeric: function(obj) { var realStringObj = obj && obj.toString(); return !jQuery.isArray(obj) && (realStringObj - parseFloat(realStringObj) + 1) >= 0; }
更新:
Angular 4.3:
export function isNumeric(value: any): boolean { return !isNaN(value - parseFloat(value)); }