创建示例项目

考察如下场景,我们有个输入框组件,输入时同时进行校验。

  1. interface IInputProps {
  2. label: string;
  3. }
  4.  
  5. function Input({ label }: IInputProps) {
  6. const [err, setErr] = useState<string | undefined>();
  7.  
  8. return (
  9. <section>
  10. {label}:
  11. <input
  12. type="text"
  13. onChange={(event) => {
  14. setErr(rulePassword(event.currentTarget.value));
  15. }}
  16. />
  17. <p>validate result:{err}</p>
  18. </section>
  19. );
  20. }
 

进行校验的逻辑使用了正则来测试:

  1. const passwrodReg = new RegExp(
  2. // eslint-disable-next-line no-useless-escape
  3. /(?!^(\d+|[a-zA-Z]+|[_\+\-=!@#\$%\^\*\(\)]+)$)^[\w_\+\-=!@#\$%\^\*\(\)]{8,64}$/,
  4. "gm"
  5. );
  6.  
  7. export const rulePassword = (value: string) => {
  8. const result = passwrodReg.test(value);
  9. console.log(`input:${value} result:${result}`);
  10. return result ? "" : "";
  11. };
 

通常,如果是密码输入框,很自然地我们会放置两个这样的输入框以让用户确保密码的一致性:

  1. function App() {
  2. return (
  3. <div className="App">
  4. <Input label="密码" />
  5. <Input label="确认密码" />
  6. </div>
  7. );
  8. }
 

对于相同的输入正则测试结果出现偏差

到此,示例写完了,运行后发现个诡谲的问题,如下图 GIF 中所展示:



  • 当我们在第一个输入框输入合法值时,显示校验结果为通过,这符合预期
  • 当我们在第二个输入框输入相同的合法值时,居然显示校验未通过
  • 进一步,当删除后再次输入时,又展示校验通过

同时,从控制台打印的日志也可重现上面的现象:

  1. input:test123123 result:true
  2. input:test123123 result:false
  3. input: result:false
  4. input:test123123 result:true
 

即,对于同样的输入 test123123,正则测试的结果居然会有偏差。

修正

当我们对校验部分的逻辑做如下变更后这个问题得以解决。

  1. - const passwrodReg = new RegExp(
  2. - // eslint-disable-next-line no-useless-escape
  3. - /(?!^(\d+|[a-zA-Z]+|[_\+\-=!@#\$%\^\*\(\)]+)$)^[\w_\+\-=!@#\$%\^\*\(\)]{8,64}$/,
  4. - "gm"
  5. - );
  6.  
  7. export const rulePassword = (value: string) => {
  8. + const passwrodReg = new RegExp(
  9. + // eslint-disable-next-line no-useless-escape
  10. + /(?!^(\d+|[a-zA-Z]+|[_\+\-=!@#\$%\^\*\(\)]+)$)^[\w_\+\-=!@#\$%\^\*\(\)]{8,64}$/,
  11. + "gm"
  12. + );
  13. const result = passwrodReg.test(value);
  14. console.log(`input:${value} result:${result}`);
  15. return result ? "" : "";
  16. };
 



所以,一定是 RegExp 缓存了什么东西,上一次的匹配结果影响了下一次。

原因

通过查看 MDN 文档发现,RegExp 通过 test() 匹配成功时,会记录当前的位置信息然后存储到 RegExplastIndex,每成功匹配一次则更新一次该字段。

并且,

Note: As long as test() returns true, lastIndex will not reset—even when testing a different string!

当配合 g 进行全局匹配时,lastIndex 是不会重置的,即使是在匹配一个全新的字符串时。

这就解释了为什么对于相同的输入,第一次匹配成功后,后面则失败了。

而当我们每次匹配都重新调用 RegExp 构造器生成正则时,就不会有这个问题了。

还有种解决方式是去掉 g 标识,每次匹配也不会复用之前的 lastIndex

相关资源

The text was updated successfully, but these errors were encountered:

JavaScript 中正则匹配时结果不一致的问题的更多相关文章

  1. JS中正则匹配的三个方法match exec test的用法

    javascript中正则匹配有3个方法,match,exec,test: match是字符串的一个方法,接收一个RegExp对象做为参数: match() 方法可在字符串内检索指定的值,或找到一个或 ...

  2. JS正则表达式一些基本使用、验证、匹配、正则匹配时一个变量

    js验证首位必须是字母 var str = "asfg"; /^[a-zA-Z].*/.test(str);//true是,false否 匹配所有空格 var str=" ...

  3. JavaScript 中定义变量时有无var声明的区别

    关于JavaScript中定义变量时有无var声明的区别 var a=5; //正确 a=5; //正确 在javascript中,以上两种方法都是定义变量的正确方法.微软的Script56.CHM中 ...

  4. Python中正则匹配使用findall时的注意事项

    在使用正则搜索内容时遇到一个小坑,百度搜了一下,遇到这个坑的还不少,特此记录一下. 比如说有一个字符串  "123@qq.comaaa@163.combbb@126.comasdf111@a ...

  5. JavaScript中正则使用

    字符串是编程时涉及到的最多的一种数据结构,对字符串进行操作的需求几乎无处不在.比如判断一个字符串是否是合法的Email地址,虽然可以编程提取@前后的子串,再分别判断是否是单词和域名,但这样做不但麻烦, ...

  6. 关于php中正则匹配包括换行符在内的任意字符的问题总结

    要使用正则匹配任意字符的话,通常有以下几种方法,这里我分别对每一种方法在使用的过程中做一个总结: 第一种方式:[.\n]*? 示例 ? PHP preg_match_all('/<div cla ...

  7. JavaScript中正则的使用(1)

    通过例子学习正则中的常见语法(1) $num javascript var a = 'javascript'; var b = a.replace(/(java)(script)/gi, '$2-$1 ...

  8. Python中正则匹配使用findall,捕获分组(xxx)和非捕获分组(?:xxx)的差异

    转自:https://blog.csdn.net/qq_42739440/article/details/81117919 下面是我在用findall匹配字符串时遇到的一个坑,分享出来供大家跳坑. 例 ...

  9. html中正则匹配img

    1.正则匹配html中的img标签,取出img的url并进行图片文件下载: /// <summary> /// 将image标签的src属性的url替换为base64 /// </s ...

随机推荐

  1. 全真教程:Windows环境Jupyter Notebook安装、运行和工作文件夹配置

    全真教程:Windows环境Jupyter Notebook安装.运行和工作文件夹配置 @ 目录 全真教程:Windows环境Jupyter Notebook安装.运行和工作文件夹配置 一.Jupyt ...

  2. MySQL入门(7)——表数据的增、删、改

    MySQL入门(7)--表数据的增.删.改 插入数据 使用INSERT···VALUES语句插入数据 INSERT语句最常用的格式是INSERT···VALUES: INSERT [LOW_PRIOR ...

  3. 《Selenium自动化测试实战:基于Python》之 Python与Selenium环境的搭建

    第2章  Python与Selenium环境的搭建 购买链接:  京东:https://item.jd.com/13123910.html  当当:http://product.dangdang.co ...

  4. 【LeetCode】10.Regular Expression Matching(dp)

    [题意] 给两个字符串s和p,判断s是否能用p进行匹配. [题解] dp[i][j]表示s的前i个是否能被p的前j个匹配. 首先可以分成3大类情况,我们先从简单的看起: (1)s[i - 1] = p ...

  5. Swagger接口如何生成Html离线文档

    A very simple tool that converts Swagger Api Document to Html File. 小记Swagger接口生成Html离线文档 由来 很多人用swa ...

  6. java例题_13 加上100再加上168的完全平方数问题

    1 /*13 [程序 13 根据条件求数字] 2 题目:一个整数,它加上 100 后是一个完全平方数,再加上 268 又是一个完全平方数,请问该数是多少? 3 程序分析:在 10万以内判断,先将该数加 ...

  7. C++并发与多线程学习笔记--参数传递详解

    传递临时对象 陷阱 总结 临时对象作为线程参数 线程id的概念 临时对象构造时的抓捕 成员函数指针做线程函数 传递临时对象作为线程参数 创建的工作线程不止一个,线程根据编号来确定工作内容.每个线程都需 ...

  8. python基础(十一):集合的使用(下)

    add() 含义:向集合中添加指定的元素: # 注:重复元素没办法加入. >>> s = {1,2,3} >>> s.add(2) >>> s{1 ...

  9. 实战 Java 16 值类型 Record - 2. Record 的基本用法

    在上一篇文章实战 Java 16 值类型 Record - 1. Record 的默认方法使用以及基于预编译生成相关字节码的底层实现中,我们详细分析了 Record 自带的属性以及方法和底层字节码与实 ...

  10. python基础(补充):python三大器之生成器

    生成器的定义 通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后 ...