详解lastindex,正则test()与全局匹配g偶遇,带来一会true一会false的坑
一、简单的需求与奇怪的问题
周一接到需求文档,产品分类页的输入框,需要加一个智能下拉提醒的功能,大概就是用户输入啥,找到符合输入字段的产品名,进行下拉推荐,同时将此字段标红,有点类似于百度搜索的智能提醒。
实现流程图一画,逻辑一理,胸有成竹就开始写代码了,然后在字段正则匹配时,却遇到一件很诡异的事情,我来拿一个简单的例子还原。
let arr = ["hello",'echo','demo','more'],
reg = new RegExp ("e",'g'),//全局匹配字母e
arr_ = [];
arr.forEach(function(item){
reg.test(item) ? arr_.push(item) : null;
});
console.log(arr_);//["hello", "demo", "more"]
这里我定义了一个包含四个字符串的数组,且每个元素都包含字母e,但最终缺只拿到了三个,echo没匹配到?啥玩意?
对逻辑产生怀疑的我立马给代码打了断点,我想看看当循环到echo时到底发生了啥,然后诡异的一幕出现了!!!!

什么情况,一会true,一会false,逻辑没按照原本的设想运行,难怪匹配的结果不符合语气,原本对此功能信心满满的我...瞬间被天降BUG锤的头都歪了..

二、尝试解决,发现lastIndex隐藏的坑
一番资料查找,发现不少人都遇到过这类问题,总结下来,都是由于不了解lastIndex而带来的问题,先贴上资料。
lastIndex只有正则表达式使用了表示全局检索的 "g" 标志,且使用exec( )或test()时,该属性才会起作用。同时,它还满足以下规则:
如果在正则匹配中成功匹配到字符串,lastIndex会被设置为第一次匹配到的字符串的位置,以作为字符串全局匹配下次检索的起点,如果后面字段还能匹配成功,那么lastIndex会被反复重新赋值,直到匹配失败,它会被重置为0;
说的有点绕,为了方便验证这句话,我们修改上面的例子,同时一步步来解析。
let str = 'hello echo',
reg = new RegExp ("e",'g'),//全局匹配字母e
arr_ = [];
reg.test(str) ? arr_.push(str) : null;
console.log(reg.lastIndex);
首先,匹配到了第一个e,e所在的位置是2,所以此时的lastIndex被修改为2,因为是全局匹配,正则以位置2为起点继续匹配,找到了第二个e,位置是7(空格也算一个位置),那么lastIndex被修改为7,作为下次匹配的起点,继续匹配,没找到,
lastIndex被修改为0,我们总结下三次匹配的状态。
全局匹配开始
第一次匹配,true--e的位置为2,所以lastIndex为2;
继续后续匹配,第二次成功匹配,true--e的位置为7,所以lastIndex为7;
继续后续匹配,false,后面找不到e,所以lastIndex被重置为0;
一轮全局匹配完成。

跟断点预期一模一样,简直完美,那么问题所在找到了,因为用了全局匹配加test,很不巧的触发了lastIndex属性,从而引发了这个巨坑。
三、回归问题,巧妙解决
那么我们回归最初的匹配问题,说到底,因为我们用了全局匹配g+test()方法,其实主要不满足其中一点,正则匹配都能达到所预期的效果了。
我们的目的是判断字符串中能不能找到规定的字符,其实我们可以采用indexOf来查询,像str.indexOf('e');
其次,单纯的判断字符串中有没有一个字母,本身是不需要全局匹配的,所以一开始的思路就错了,去掉全局匹配的g也能解决问题。
如果不想放弃g,那就不用text()也行,毕竟我们还有match()方法。
//保留全局匹配,要那个match代替test方法
let arr = ["hello",'echo','demo','more'],
reg = new RegExp ("e",'g'),//全局匹配字母e
arr_ = [];
arr.forEach(function(item){
item.match(reg) ? arr_.push(item) : null;
});
console.log(arr_);//["hello", "echo", "demo", "more"] //直接使用indexOf查找有没有
let arr = ["hello",'echo','demo','more'],
arr_ = [];
arr.forEach(function(item){
item.indexOf('e') !== -1? arr_.push(item) : null;
});
console.log(arr_);//["hello", "echo", "demo", "more"]
解决的办法灵活多变,前提弄清楚问题所在尤为重要,那么现在,我们都可以对着lastindex说一句,拜托,你很弱哎。

参考资料:
详解lastindex,正则test()与全局匹配g偶遇,带来一会true一会false的坑的更多相关文章
- java.util.regex包下的Pattern和Matcher详解(正则匹配)
java正则表达式通过java.util.regex包下的Pattern类与Matcher类实现(建议在阅读本文时,打开java API文档,当介绍到哪个方法时,查看java API中的方法说明,效果 ...
- iptables详解(5):iptables匹配条件总结之二(常用扩展模块)
所属分类:IPtables Linux基础 在本博客中,从理论到实践,系统的介绍了iptables,如果你想要从头开始了解iptables,可以查看iptables文章列表,直达链接如下 iptab ...
- iptables详解(4):iptables匹配条件总结之一
所属分类:IPtables Linux基础 在本博客中,从理论到实践,系统的介绍了iptables,如果你想要从头开始了解iptables,可以查看iptables文章列表,直达链接如下 iptab ...
- 详解CSS选择器、优先级与匹配原理
原文链接:http://polaris1119.javaeye.com/blog/764428 作为一个Web开发者,掌握必要的前台技术也是很重要的,特别是在遇到一些实际问题的时候.这里给大家列举一个 ...
- 转载:详解CSS选择器、优先级与匹配原
转载网址:http://polaris1119.javaeye.com/blog/764428 文章就CSS选择器的优先级问题做了一些总结,严格来讲,选择器的种类可以分为三种:标签名选择器.类选择器和 ...
- 详解CSS选择器、优先级与匹配原理【转】
作为一个Web开发者,掌握必要的前台技术也是很重要的,特别是在遇到一些实际问题的时候.这里给大家列举一个例子: 给一个p标签增加一个类(class),可是执行后该class中的有些属性并没有起作用.通 ...
- git log --author详解,这个是个模糊匹配
git log --author=authorname --author=<pattern>, commits whose author matches any of the given ...
- SASS和SCSS标签详解与scoped局部和全局的使用
首先,学会使用sass: 1.先下载和安装node-sass和一些加载器 $ cnpm install sass-loader node-sass vue-style-loader --D 2.配置w ...
- 详解为什么设置overflow为hidden可以清除浮动带来的影响
1.问题起源 在平时的业务开发写CSS中,为了满足页面布局,元素的浮动特性我们用的不能再多了.使用浮动的确能够解决一些布局问题,但是也带了一些副作用影响,比如,父元素高度塌陷,我们有好几种可以清除浮动 ...
随机推荐
- QOpenGLFunctions的使用(2)
QOpenGLFunctions的使用(2) 前一小结请参考:QOpenglFuncations(1) www.icmzn.com 本小节介绍相关的类: 1. The QGLContext class ...
- POJ2779 线性DP 或 杨氏三角 和 钩子公式
POJ2779 线性DP 或 杨氏三角 和 钩子公式 本来就想回顾一下基础的线性DP谁知道今早碰到的都是这种大难题,QQQQ,不会 这个也没有去理解线性DP的解法,了解了杨氏三角和钩子公式,做出了PO ...
- JVM可支持的最大线程数
转微博,因为他也是转载 不知道原出处 一.认识问题: 首先我们通过下面这个 测试程序 来认识这个问题:运行的环境 (有必要说明一下,不同环境会有不同的结果):32位 Windows XP,Sun J ...
- cocos 2dx 通过循环实现界面图形的摆放
首先创建一个一维数组 this.starSprites = new Array(); 然后知道星星的间距和坐标后通过如下代码实现位置的摆放 for(var i = 0; i < 6; i++){ ...
- XE7 & FMX 那些年我们一起上过的控件:ListView 之 (3) 加载数据时如何显示自定义样式
本文介绍一下ListView下如何加载数据.及使用进度条反馈当前进度给用户. 注意: 原创作品,请尊重作者劳动成果,转载请注明出处!!!原文永久固定地址:http://www.cnblogs.com/ ...
- 封装了三个对TMemoryStream操作的函数,大牛莫笑
// TMemoryStream 转化为string字符串 function MemoryStreamToString(M: TMemoryStream): AnsiString; begin Set ...
- asp.net core 健康检查
asp.net core 健康检查 ASP.NET Core 2.2 开始,提供了健康检查中间件和库,用来报告应用基础结构组件的运行状况.官方文档在此 运行状况检查由应用程序作为 HTTP 终结点公开 ...
- MVVM 简化的Messager类
看MVVMLight的Messager源码,自己实现了一个简单的Messager类. Messager类可以在MVVM中,实现View与VM.VM与VM.View与View的通信. public cl ...
- Storm入门示例
开发Storm的第一步就是设计Topology,为了方便开发者入门,首先我们设计一个简答的例子,该例子的主要的功能就是把每个单词的后面加上Hello,World后缀,然后再打印输出,整个例子的Topo ...
- Swift5 语言指南(二十一) 嵌套类型
通常创建枚举以支持特定类或结构的功能.类似地,定义纯粹在更复杂类型的上下文中使用的实用程序类和结构可能是方便的.为此,Swift允许您定义嵌套类型,从而在它们支持的类型定义中嵌套支持枚举,类和结构. ...