在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

当前回答

如果您希望让数值函数的预测值隐式严格(例如,不解析字符串),那么这应该会奏效。

function isNumeric(n, parse) {
    var t = typeof(n);
    if (parse){
        if (t !== 'number' && t !=='string') return false;
        return !isNaN(parseFloat(n)) && isFinite(n);
    }else{
        if (t !== 'number') return false;
        return !isNaN(n) && isFinite(n) && !_.isString(n);
    }
}

function isInteger(n, parse) {    
    return isNumeric(n, parse) && n % 1 === 0;
}

function isFloat(n, parse) {
    return isNumeric(n, parse) && n % 1 !== 0;
}

如果您希望代码解析字符串,那么只需在parse参数中传递true即可。

这是对下划线contrib方法的修改,该方法隐式宽松,尝试解析字符串,甚至为isNumeric([1])返回true,这可能是人们真正的陷阱。我上面的方法也会更快,因为它只在parse=true时调用parseFloat()。

其他回答

最好的方法如下:

function isThisActuallyANumber(data){
    return ( typeof data === "number" && !isNaN(data) );
}

被接受的答案没有通过你的7号考试,我想这是因为你改变了主意。所以这是对公认答案的回应,我对此有异议。

在一些项目中,我需要验证一些数据,并尽可能确定它是可以用于数学运算的javascript数值。

jQuery和其他一些javascript库已经包含了这样一个函数,通常称为isNumeric。在stackoverflow上也有一篇文章被广泛接受作为答案,这与前面提到的库所使用的一般例程相同。

function isNumber(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

首先,如果参数是长度为1的数组,并且单个元素的类型被上述逻辑视为数字,则上述代码将返回true。在我看来,如果它是一个数组,那么它就不是数字。

为了缓解这个问题,我从逻辑中添加了一个对折扣数组的检查

function isNumber(n) {
  return Object.prototype.toString.call(n) !== '[object Array]' &&!isNaN(parseFloat(n)) && isFinite(n);
}

当然,您也可以使用Array.isArray、jquery$.isArray或原型Object.isArra,而不是Object.protype.toString.call(n)!=='[object数组]'

我的第二个问题是,负十六进制整数文本字符串(“-0xA”->-10)没有被计算为数字。但是,正十六进制整数文本字符串(“0xA”->10)被视为数字。我需要两者都是有效的数字。

然后我修改了逻辑以将其考虑在内。

function isNumber(n) {
  return Object.prototype.toString.call(n) !== '[object Array]' &&!isNaN(parseFloat(n)) && isFinite(n.toString().replace(/^-/, ''));
}

如果您担心每次调用函数时都会创建正则表达式,那么可以在闭包中重写它,如下所示

var isNumber = (function () {
  var rx = /^-/;
   
  return function (n) {
      return Object.prototype.toString.call(n) !== '[object Array]' &&!isNaN(parseFloat(n)) && isFinite(n.toString().replace(rx, ''));
  };
}());

然后,我采用了CMS+30测试用例,并在jsfiddle上克隆了测试,并添加了额外的测试用例和上述解决方案。

它可能不会取代广泛接受/使用的答案,但如果这是您期望的isNumeric函数的结果,那么希望这会有所帮助。

编辑:正如Bergi所指出的,还有其他可能的对象可以被视为数字对象,最好将其列入白名单,而不是列入黑名单。考虑到这一点,我将增加标准。

我希望我的isNumeric函数只考虑数字或字符串

考虑到这一点,最好使用

function isNumber(n) {
  return (Object.prototype.toString.call(n) === '[object Number]' || Object.prototype.toString.call(n) === '[object String]') &&!isNaN(parseFloat(n)) && isFinite(n.toString().replace(/^-/, ''));
}

测试解决方案

var testHelper=函数(){var testSuite=函数(){test(“Integer Literals”,函数(){ok(isNumber(“-10”),“负整数字符串”);ok(isNumber(“0”),“零字符串”);ok(isNumber(“5”),“正整数字符串”);ok(isNumber(-16),“负整数”);ok(isNumber(0),“零整数”);ok(isNumber(32),“正整数”);ok(isNumber(“040”),“八进制整数字符串”);ok(isNumber(0144),“八进制整数文字”);ok(isNumber(“-040”),“负八进制整数字符串”);ok(isNumber(-0144),“负八进制整数文字”);ok(isNumber(“0xFF”),“十六进制整数字符串”);ok(isNumber(0xFFF),“十六进制整数文字”);ok(isNumber(“-0xFF”),“负十六进制整数字符串”);ok(isNumber(-0xFFF),“负十六进制整数文字”);});测试(“Foating Point Literals”,函数(){ok(isNumber(“-1.6”),“负浮点字符串”);ok(isNumber(“4.536”),“正浮点字符串”);ok(isNumber(-2.6),“负浮点数”);ok(isNumber(3.1415),“正浮点数”);ok(isNumber(8e5),“指数表示法”);ok(isNumber(“123e-2”),“指数符号字符串”);});测试(“非数值”,函数(){equals(isNumber(“”),false,“空字符串”);equals(isNumber(“”),false,“空白字符串”);equals(isNumber(“\t\t”),false,“制表符字符串”);等于(isNumber(“abcdefghijklm123467890”),false,“字母数字字符串”);等于(isNumber(“xabcdefx”),false,“非数字字符串”);equals(isNumber(true),false,“Boolean true literal”);equals(isNumber(false),false,“Boolean false literal”);等于(isNumber(“bcfed5.2”),false,“前面有非数字字符的数字”);等于(isNumber(“7.2acdgs”),false,“带尾随非数字字符的数字”);equals(isNumber(undefined),false,“未定义值”);equals(isNumber(null),false,“null值”);等于(isNumber(NaN),false,“NaN值”);equals(isNumber(无限),false,“无限基元”);等于(isNumber(Number.POSITIVE_INFINITY),false,“正无穷大”);等于(isNumber(Number.NEGATIVE_INFINITY),false,“负无穷大”);等于(isNumber(新日期(2009,1,1)),false,“日期对象”);equals(isNumber(new Object()),false,“空对象”);equals(isNumber(function(){}),false,“函数的实例”);equals(isNumber([]),false,“空数组”);equals(isNumber([“-10”]),false,“数组负整数字符串”);equals(isNumber([“0”]),false,“Array Zero string”);equals(isNumber([“5”]),false,“数组正整数字符串”);等于(isNumber([-16]),false,“数组负整数”);equals(isNumber([0]),false,“Array Zero integer number”);equals(isNumber([32]),false,“数组正整数”);equals(isNumber([“040”]),false,“数组八进制整数字符串”);equals(isNumber([0144]),false,“数组八进制整数文字”);equals(isNumber([“-040”]),false,“数组负八进制整数字符串”);equals(isNumber([-0144]),false,“数组负八进制整数文字”);equals(isNumber([“0xFF”]),false,“数组十六进制整数字符串”);equals(isNumber([0xFFF]),false,“数组十六进制整数文字”);equals(isNumber([“-0xFF”]),false,“数组负十六进制整数字符串”);等于(isNumber([-0xFFF]),false,“数组负十六进制整数文字”);equals(isNumber([1,2]),false,“具有1个以上正整数的数组”);equals(isNumber([-1,-2]),false,“具有1个以上负整数的数组”);});}var函数ToTest=[函数(n){回来isNaN(parseFloat(n))&&isFinite(n);},函数(n){回来isNaN(n)&&!isNaN(parseFloat(n));},函数(n){回来isNaN((n));},函数(n){回来isNaN(parseFloat(n));},函数(n){返回类型(n)!=“布尔”&&!isNaN(n);},函数(n){return parseFloat(n)==数字(n);},函数(n){return parseInt(n)==数字(n);},函数(n){回来isNaN(数字(字符串(n)));},函数(n){回来isNaN(+(“”+n));},函数(n){返回(+n)==n;},函数(n){返回n&&/^-?\d+(\.\d+)?$/。测试(n+“”);},函数(n){return isFinite(数字(字符串(n)));},函数(n){return isFinite(字符串(n));},函数(n){回来isNaN(n)&&!isNaN(parseFloat(n))&&isFinite(n);},函数(n){返回parseFloat(n)==n;},函数(n){return(n-0)==n&&n长度>0;},函数(n){返回类型n==“number”&&isFinite(n);},函数(n){回来Array.isArray(n)&&!isNaN(parseFloat(n))&&isFinite(n.toString().replace(/^-/,“”));}];//检查函数ToTest数组,提取每个函数的返回语句//并填充toTest select元素。var fillToTestSelect=函数(){对于(var i=0;i<functionsToTest.length;i++){var f=函数ToTest[i].toString();var option=/[\s\s]*return([\s\s]*);/。执行(f)[1];$(“#toTest”).append('<option value=“'+i+'”>'+(i+1)+'.'+option+'</option>');}}var performTest=函数(函数编号){reset();//重置上一测试$(“#tests”).html(“”)//清洁测试结果isNumber=函数ToTest[函数编号];//用要测试的函数重写isNumber全局函数testSuite();//运行测试//获取测试结果var totalFail=0;v

要添加的几个测试:

IsNumeric('01.05') => false
IsNumeric('1.') => false
IsNumeric('.') => false

我想到了这个:

function IsNumeric(input) {
    return /^-?(0|[1-9]\d*|(?=\.))(\.\d+)?$/.test(input);
}

解决方案包括:

开头的可选负号一个零,或者一个或多个不以0开头的数字,或者只要后面有一个周期,就不加任何数字后跟一个或多个数字的句点

使用正则表达式,我们可以涵盖问题中提出的所有情况。这里是:

所有整数和小数均为Numeric:

const isNumeric = num => /^-?[0-9]+(?:\.[0-9]+)?$/.test(num+'');

仅整数为isInteger:

const isInteger = num => /^-?[0-9]+$/.test(num+'');

我找到了简单的解决方案,可能不是最好的,但效果很好:)

所以,我接下来要做的是,将字符串解析为Int,并检查新变量的长度大小(现在是Int类型)是否和原始字符串变量的长度相同。从逻辑上讲,如果大小相同,则意味着字符串被完全解析为int,并且只有当字符串仅由数字“构成”时,这才是可能的。

var val=1+$(e).val()+'';
var n=parseInt(val)+'';
if(val.length == n.length )alert('Is int');

您可以很容易地将该代码放在函数中,如果int,则使用return true代替alert。记住,如果您在字符串中使用点或逗号,您将检查它是否仍然为false,因为您正在解析为int。

注意:在e.val上添加1+,因此不会删除起始零。