• 详解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说一句,拜托,你很弱哎。

    参考资料:

    MDN RegExp.lastIndex

  • 相关阅读:
    python3 访问 rabbitmq 示例
    centos7 GNOME 安装微信客户端
    使用 rm -rf 删除了工程目录,然后从 pycharm 中找了回来
    主动做事,做一个靠谱的人
    Go net/http 发送常见的 http 请求
    学会感激,才能长大
    Go context 介绍和使用
    xargs 命令
    Docker 镜像 && 容器的基本操作
    CentOS && Ubuntu 环境下 Docker 的安装配置
  • 原文地址:https://www.cnblogs.com/echolun/p/9649578.html
Copyright © 2020-2023  润新知