在网页中,我们从用户输入的内容中获取的值通常是字符串,但是有时候我们希望用户输入的内容一定要能转成数值:

<input id="userInput">

userInput.addEventListener('change', (e) => {

  const value = e.target.value;

  console.log(typeof value); // string

  console.assert(isNumeric(value), `Not a numeric value: ${value}`);

});

即我们要实现一个isNumeric方法,判断用户输入的值是能转为数值的字符串。

我们讨论isNumeric实现前,先说一下限制用户输入的方式。

?? 如果我们设置input的type为number,并不能保证输入的内容一定是数值,因为如果input的type是number,它依然可以输入多个“+“、”-”、“.”、“e”

<input type="number" step="0.0000001" id="userInput">

input[type=number]并不阻止输入多个e

这是因为“+/-”(正负符号),“.”(小数点)和“e”(科学记数法)都是Number允许输入的字符。

不过如果在form提交的时候,浏览器会对input[type=number]内容再做一次检查:

<form id="myForm">

  <input type="number">

  <input type="submit">

form>

但是,不管怎样,用户还是可以通过修改页面上的元素,绕过这些检查,所以我们还是要用到isNumeric来判断用户输入的合法性。

我们先看一下isNumeric应该返回什么。

如果参考input[type=number]的规则,那么它应该支持所有合法的有穷数值写法:

function isNumeric(str) {

  ...

}

console.assert(isNumeric('1000'));

console.assert(isNumeric('-100.'));

console.assert(isNumeric('.1'));

console.assert(isNumeric('-3.2'));

console.assert(isNumeric('001'));

console.assert(isNumeric('+4.5'));

console.assert(isNumeric('1e3'));

console.assert(isNumeric('1e-3'));

console.assert(isNumeric('-100e-3'));

console.assert(!isNumeric('++3'));

console.assert(!isNumeric('-100..'));

console.assert(!isNumeric('3abc'));

console.assert(!isNumeric('abc'));

console.assert(!isNumeric('-3e3.2'));

console.assert(!isNumeric('Infinity'));

console.assert(!isNumeric('-Infinity'));

console.assert(!isNumeric(''));

那么具体要怎么实现呢?

parseFloat?

有同学想到用parseFloat,这个行不行呢?

function isNumeric(str) {

  return !Number.isNaN(parseFloat(str));

}

这个显然是不行的,因为parseFloat('123abc')结果是123,因为parseFloat会尝试转部分数值,而忽略掉不能转数值的部分。

所以:

console.assert(!isNumeric('-100..'));

console.assert(!isNumeric('3abc'));

console.assert(!isNumeric('-3e3.2'));

这三个case是过不去的,另外这里用了Number.isNaN处理parseFloat之后的结果,由于±Infinity是数值,Number.isNaN会返回false,所以:

console.assert(!isNumeric('Infinity'));

console.assert(!isNumeric('-Infinity'));

也pass不了。

isNaN

有同学说,那我们直接使用isNaN如何?

function isNumeric(str) {

  return !isNaN(str);

}

这次结果好得多,但是最后三条规则过不了:

console.assert(!isNumeric('Infinity'));

console.assert(!isNumeric('-Infinity'));

console.assert(!isNumeric(''));

±Infinity和上面的原因一样,但是为什么''也pass不了呢?这是因为isNaN会先尝试将参数转为Number,而空字符串被转为了数值0。

console.log(Number('')); // 0

这里面就不得不提一下ECMA-262规范里面[[ToNumber]]的转换规则了:

根据规则,Null、Boolean都会转成Number,Undefined被转成NaN,Undefined会被转成NaN,而Symbol直接抛TypeError...

加上空字符串''被转成0,isNaN就会有些怪异的行为了:

console.log(isNaN(undefined)); // true

console.log(isNaN(null)); // false

console.log(isNaN(true)); // false

console.log(isNaN(false)); // false

console.log(isNaN('')); // false

其实字符串除了''还有一些:

console.log(isNaN(' ')); // false

console.log(isNaN(' ')); // false

console.log(isNaN('\t')); // false

console.log(isNaN('\r')); // false

console.log(isNaN('\n')); // false

这就是为什么ES2015之后,又增加了Number.isNaN方法。

?? 冷知识:isNaN方法对参数做[[ToNumber]]转换,会导致一些比较怪异的结果,所以ES2015增加了Number.isNaN,该方法不会对参数做类型转换,只要参数不是NaN,不管是什么类型,Number.isNaN一律返回false。

console.log(isNaN('abc')); // true

console.log(Number.isNaN('abc')); // false

console.log(isNaN('')); // false

console.log(Number.isNaN('')); // false

isFinite

我们把isNaN换成isFinite看看:

function isNumeric(str) {

  return isFinite(str);

}

这下'±Infinity'的问题解决了,因为Number中的±Infinite和NaN的isFinite结果都返回false。

不过与isNaN一样,isFinite也一样会对参数进行类型转换,所以,这几个case问题还是存在:

console.assert(!isNumeric(''));

console.assert(!isNumeric(' '));

console.assert(!isNumeric(' '));

console.assert(!isNumeric('\t'));

console.assert(!isNumeric('\r'));

console.assert(!isNumeric('\n'));

?? 冷知识:isFinite与isNaN一样,会对参数做[[ToNumber]]转换,因此对应的,ES2015也提供了一个Number.isFinite,这是不转换参数类型的版本。如果参数不是Number类型,Number.isFinite一律返回false。

console.log(isFinite('123')); // true

console.log(Number.isFinite('123')); // false

console.log(isFinite('')); // true

console.log(Number.isFinite('')); // false

好了,那么讨论到这里,最后的解决方法已经呼之欲出了。

因为对于isNumeric用法,我们只需要处理字符串,非字符串的case我们可以不管;那么我们剩下的就是处理这一堆字符串case:

console.assert(!isNumeric(''));

console.assert(!isNumeric(' '));

console.assert(!isNumeric(' '));

console.assert(!isNumeric('\t'));

console.assert(!isNumeric('\r'));

console.assert(!isNumeric('\n'));

这个有很多方式可以处理了,比如它们都匹配正则/^\s*$/,所以:

function isNumeric(str) {

  return !/^\s*$/.test(str) && isFinite(str);

}

这个版本就可以通过所有的case了。

另外,这些字符串的parseFloat都是NaN,所以,也可以这样:

function isNumeric(obj) {

  return !isNaN(parseFloat(obj)) && isFinite(obj);

}

实际上这个比上面那个正则的版本更好,因为这个还同时处理了非字符串的case,因为:

parseFloat(null);

parseFloat(true);

parseFloat(false);

上面这些的结果都是NaN。

实际上,上面这个版本就是著名的jQuery框架中的jQuery.isNumeric的实现方式。

因为现在不建议用isNaN和isFinite,而推荐使用Number.isNaN和Number.isFinite替代,所以一些linter的规则可能会禁止使用这两个函数,但是没有关系,因为我们可以这么写:

function isNumeric(obj) {

  return !Number.isNaN(parseFloat(obj))

    && Number.isFinite(Number(obj));

}

所以,这个就是最终的版本。

原来,实现一个小小的函数isNumeric,有那么多需要注意的地方。

关于判断字符串是数值,你还有什么想法,欢迎在issue中讨论。

excel判断字符串包含另一个字符串_【前端冷知识】如何正确判断一个字符串是数值?...相关推荐

  1. bootstrap 轮播控制时间_【前端冷知识】如何封装一个图片轮播组件

    组件封装是一个前端工程师进阶的必经之路.组件封装是指Web页面上抽出来一个个包含模版(HTML).功能(Javascript)和样式(CSS)的单元.所以,今天的内容,我们将带你了解组件封装的开发思路 ...

  2. 一个对象的属性_【前端冷知识】如何判断一个对象的某个属性是可写的?

    这是一个咋一听好像很简单,但是实际上却没那么简单,而且是很有趣的问题. 我们先来看一下什么情况下一个对象的属性是可写的. "属性可写"这个概念并没有严谨的定义,我们这里先来规定一下 ...

  3. 前端里的button怎么去除点击自带边框_前端不为人知的一面--前端冷知识集锦

    前端已经被玩儿坏了!像console.log()可以向控制台输出图片等炫酷的玩意已经不是什么新闻了,像用||操作符给变量赋默认值也是人尽皆知的旧闻了,今天看到Quora上一个帖子,瞬间又GET了好多前 ...

  4. 前端发送的字符串有大小限制吗_前端经典面试题 30道

    30 道前端面试题包含 JS.CSS.React.网络.浏览器.程序题等出处:https://segmentfault.com/a/1190000020391424 题 1 :什么是防抖和节流?有什么 ...

  5. 字符串类型的数字比较大小_Python核心知识系列:数字与字符串类型

    ​学习任何一种编程语言时首先要对它的数据类型有所了解,从本篇开始会对 Python 中的常见数据类型的基本概念和操作进行介绍. Python中有6种标准的数据类型:数字(Number).字符串(Str ...

  6. react中创建一个组件_如何使用React和MomentJS创建一个Countdown组件

    react中创建一个组件 Recently I had to create a Countdown for one of my other projects, and I thought that i ...

  7. java按钮触发另一个页面_前端跨页面通信,你知道哪些方法?

    戳蓝字「前端技术优选」关注我们哦! 引言 在浏览器中,我们可以同时打开多个Tab页,每个Tab页可以粗略理解为一个"独立"的运行环境,即使是全局对象也不会在多个Tab间共享.然而有 ...

  8. 前端wxml取后台js变量值_这些鲜为人知的前端冷知识,你都GET了吗?

    背景 最近公司项目不多,比较清闲,划水摸鱼混迹于各大技术博客平台,瞬间又GET了好多前端技能,一些属于技巧,一些则是闻所未闻的冷知识,一时间还消化不过来,不由的发出一声感叹! 前端可真是博大精深 于是 ...

  9. 判断输入的字符串是否为回文_刷题之路(九)--判断数字是否回文

    Palindrome Number 问题简介:判断输入数字是否是回文,不是返回0,负数返回0 举例: 1: 输入: 121 输出: true 2: 输入: -121 输出: false 解释: 回文为 ...

最新文章

  1. 深入解析Python中的变量和赋值运算符
  2. 【搜索专题】DFS之连通性模型与搜索顺序
  3. 3Blue1Brown:“线性代数的本质”完整笔记
  4. V8 Profiler 揭秘
  5. 两个Java项目之间的通信_两个容器之间的Docker通信与Java
  6. HashMap和LinkedHashMap的比较使用
  7. Redis安装-win-linux-mac
  8. Shiro与Spring集成时,Shiro权限注解@RequiresRoles等不生效的解决方案
  9. Ubuntu14.04安装及配置mysql5.7.19
  10. 为什么数据库使用有序索引,而程序员却在使用哈希表?
  11. 再论《IT人员应聘建议》
  12. 医院药品管理系统java sql_医院药品管理系统设计(Netbeans,Myeclipse,MySQL,SQLServer)
  13. 联想打印机 android,小新联想打印机
  14. h5是可以一键打包小程序的_Vue项目快速输出到小程序H5-如何将h5打包成小程序-h5小程序怎么做...
  15. 云上财务经营的成本管理
  16. Http request传输图片和附属信息(old)
  17. C Primer Plus 第2章 课后答案
  18. VxWorks6.7新建bootrom工程
  19. 如何修改电脑用户文件名
  20. 向量或矩阵的微分计算

热门文章

  1. 操作系统原理:死锁的特征,预防,避免,恢复
  2. python3安装setuptools步骤_linux环境下的python安装过程(含setuptools)
  3. C#session共享+redis_Shiro权限管理框架(二):Shiro结合Redis实现分布式环境下的Session共享...
  4. OS / Linux / pthread_cond_wait 为什么需要传递 mutex 参数?
  5. 网站改成静态页面打不开_稳定网站排名的基本条件 - 最蜘蛛池外推快速收录
  6. word中安装MathType
  7. java字符排序规则_java 重写排序规则,用于代码层级排序
  8. 如何将zipoutputstream返回_excel:vlookup如何区间查找?如何使用通配符?如何多条件查找?...
  9. 前端常见知识点五之Fetch
  10. python多线程爬取_python 多线程方法爬取微信公众号文章