Open wayou opened 3 years ago
考察如下场景,我们有个输入框组件,输入时同时进行校验。
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,正则测试的结果居然会有偏差。
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 缓存了什么东西,上一次的匹配结果影响了下一次。
RegExp
通过查看 MDN 文档发现,RegExp 通过 test() 匹配成功时,会记录当前的位置信息然后存储到 RegExp 的 lastIndex,每成功匹配一次则更新一次该字段。
test()
lastIndex
并且,
Note: As long as test() returns true, lastIndex will not reset—even when testing a different string!
当配合 g 进行全局匹配时,lastIndex 是不会重置的,即使是在匹配一个全新的字符串时。
g
这就解释了为什么对于相同的输入,第一次匹配成功后,后面则失败了。
而当我们每次匹配都重新调用 RegExp 构造器生成正则时,就不会有这个问题了。
还有种解决方式是去掉 g 标识,每次匹配也不会复用之前的 lastIndex。
JavaScript 中正则匹配时结果不一致的问题
创建示例项目
考察如下场景,我们有个输入框组件,输入时同时进行校验。
进行校验的逻辑使用了正则来测试:
通常,如果是密码输入框,很自然地我们会放置两个这样的输入框以让用户确保密码的一致性:
对于相同的输入正则测试结果出现偏差
到此,示例写完了,运行后发现个诡谲的问题,如下图 GIF 中所展示:

同时,从控制台打印的日志也可重现上面的现象:
即,对于同样的输入
test123123
,正则测试的结果居然会有偏差。修正
当我们对校验部分的逻辑做如下变更后这个问题得以解决。

所以,一定是
RegExp
缓存了什么东西,上一次的匹配结果影响了下一次。原因
通过查看 MDN 文档发现,
RegExp
通过test()
匹配成功时,会记录当前的位置信息然后存储到RegExp
的lastIndex
,每成功匹配一次则更新一次该字段。并且,
当配合
g
进行全局匹配时,lastIndex
是不会重置的,即使是在匹配一个全新的字符串时。这就解释了为什么对于相同的输入,第一次匹配成功后,后面则失败了。
而当我们每次匹配都重新调用
RegExp
构造器生成正则时,就不会有这个问题了。还有种解决方式是去掉
g
标识,每次匹配也不会复用之前的lastIndex
。相关资源