創建示例項目
考察如下場景,我們有個輸入框組件,輸入時同時進行校驗。
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
。
相關資源
- Javascript Regex test same string but got different result [duplicate]
- MDN - Using test() on a regex with the "global" flag
The text was updated successfully, but these errors were encountered: