在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

当前回答

被接受的答案没有通过你的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

其他回答

function isNumeric(n) {
    var isNumber = true;

    $.each(n.replace(/ /g,'').toString(), function(i, v){
        if(v!=',' && v!='.' && v!='-'){
            if(isNaN(v)){
               isNumber = false;
               return false;
            }
         }
     });

    return isNumber;
}

isNumeric(-3,4567.89);   // true <br>

isNumeric(3,4567.89);   // true <br>

isNumeric("-3,4567.89");   // true <br>

isNumeric(3d,4567.89);   // false

如果我没有弄错,这应该匹配任何有效的JavaScript数值,不包括常量(Infinity,NaN)和符号运算符+/-(因为就我而言,它们实际上不是数字的一部分,它们是独立的运算符):

我需要一个令牌化器,将数字发送到JavaScript进行评估不是一个选项。。。它肯定不是最短的正则表达式,但我相信它抓住了JavaScript数字语法的所有细微之处。

/^(?:(?:(?:[1-9]\d*|\d)\.\d*|(?:[1-9]\d*|\d)?\.\d+|(?:[1-9]\d*|\d)) 
(?:[e]\d+)?|0[0-7]+|0x[0-9a-f]+)$/i

有效数字包括:

 - 0
 - 00
 - 01
 - 10
 - 0e1
 - 0e01
 - .0
 - 0.
 - .0e1
 - 0.e1
 - 0.e00
 - 0xf
 - 0Xf

无效数字将为

 - 00e1
 - 01e1
 - 00.0
 - 00x0
 - .
 - .e0

可以使用类型检查库,如https://github.com/arasatasaygin/is.js或者从那里提取一个检查片段(https://github.com/arasatasaygin/is.js/blob/master/is.js#L131):

is.nan = function(value) {    // NaN is number :) 
  return value !== value;
};
 // is a given value number?
is.number = function(value) {
    return !is.nan(value) && Object.prototype.toString.call(value) === '[object Number]';
};

通常,如果您需要它来验证参数类型(在函数调用的入口点),您可以使用符合JSDOC的契约(https://www.npmjs.com/package/bycontract):

/**
 * This is JSDOC syntax
 * @param {number|string} sum
 * @param {Object.<string, string>} payload
 * @param {function} cb
 */
function foo( sum, payload, cb ) {
  // Test if the contract is respected at entry point
  byContract( arguments, [ "number|string", "Object.<string, string>", "function" ] );
}
// Test it
foo( 100, { foo: "foo" }, function(){}); // ok
foo( 100, { foo: 100 }, function(){}); // exception

要添加的几个测试:

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

我想到了这个:

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

解决方案包括:

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

以下方法在许多情况下似乎都很有效:

function isNumeric(num) {
    return (num > 0 || num === 0 || num === '0' || num < 0) && num !== true && isFinite(num);
}

这是建立在这个答案之上的(也是这个答案的基础):https://stackoverflow.com/a/1561597/1985601