创建示例项目

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

interface IInputProps {
label: string;
} function Input({ label }: IInputProps) {
const [err, setErr] = useState<string | undefined>(); return (
<section>
{label}:
<input
type="text"
onChange={(event) => {
setErr(rulePassword(event.currentTarget.value));
}}
/>
<p>validate result:{err}</p>
</section>
);
}
 

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

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

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

function App() {
return (
<div className="App">
<Input label="密码" />
<Input label="确认密码" />
</div>
);
}
 

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

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





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

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

input:test123123 result:true
input:test123123 result:false
input: result:false
input:test123123 result:true
 

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

修正

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

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



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

原因

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

并且,

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. Reactive Spring实战 -- 响应式Redis交互

    本文分享Spring中如何实现Redis响应式交互模式. 本文将模拟一个用户服务,并使用Redis作为数据存储服务器. 本文涉及两个java bean,用户与权益 public class User ...

  2. 看了 Spring 官网脚手架真香,也撸一个 SpringBoot DDD 微服务的脚手架!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 为什么我们要去造轮子? 造轮子的核心目的,是为了解决通用共性问题的凝练和复用. 虽然 ...

  3. 一键获取linux内存、cpu、磁盘IO等信息脚本编写,及其原理详解

    更多linux知识,请关注公众号:一口Linux 一.脚本 今天主要分享一个shell脚本,用来获取linux系统CPU.内存.磁盘IO等信息. #!/bin/bash # 获取要监控的本地服务器IP ...

  4. 【JVM进阶之路】一:Java虚拟机概览

    1.Java简史 Java语言是一门通用的.面向对象的.支持并发的程序语言.全球从事Java相关开发的人员已经数以百万计. 从1995年"Java"正式出现以来,Java已经经历了 ...

  5. PTA 二叉树的三种遍历(先序、中序和后序)

    6-5 二叉树的三种遍历(先序.中序和后序) (6 分)   本题要求实现给定的二叉树的三种遍历. 函数接口定义: void Preorder(BiTree T); void Inorder(BiTr ...

  6. 11、Spring教程之声明式事务

    1.回顾事务 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎! 事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性. 事务就是把一系列的动作当成一个独立的工作单元,这 ...

  7. redis数据库如何批量删除键和设置过期时间?

    我们可以借助Linux中的xargs,在终端中执行命令来实现这两个功能. 一.批量删除键 批量删除以"key"开头key的方法,需要借助Linux中的xargs,在终端中执行以下命 ...

  8. Python数据分析入门(十):数据清洗和准备

    数据清洗是数据分析关键的一步,直接影响之后的处理工作 数据需要修改吗?有什么需要修改的吗?数据应该怎么调整才能适用于接下来的分析和挖掘? 是一个迭代的过程,实际项目中可能需要不止一次地执行这些清洗操作 ...

  9. CVE-2010-3333-office RTF栈溢出漏洞分析

    0x00 前言 此漏洞是根据泉哥的<漏洞战争>来学习分析的,网上已有大量分析文章在此只是做一个独立的分析记录. 0x01 复现环境 操作系统-->windows7 x64 软件版本- ...

  10. 【笔记】《Redis设计与实现》chapter8 对象

    8.1 对象的类型与编码 Redis中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的三个属性分别是type属性.encoding属性和ptr属性 typedef struc ...