現象:
var patt = /^[1-9]\d{0,2}(.\d{0,2})?$/gi;
patt.test("111.12"); // true
patt.test("111.12"); // false ← 陷阱
patt.test("111.12"); // true
根因
- 正則帶 `g` (global) 旗標時,`test()` / `exec()` 會**從 `lastIndex` 位置開始匹配**,匹配成功後 `lastIndex` 更新為匹配文本末尾索引。
- `^...$` 錨定行首行尾,第二次調用時 `lastIndex > 0`,從字符串中間開始匹配,`^` 不匹配 → 失敗 → `lastIndex` 重置為 0 → 第三次又成功。
解決方案
- 移除 `g` 標誌(最簡單):`var patt = /^[1-9]\d{0,2}(.\d{0,2})?$/i;` — 無全局匹配需求時**永遠不要加 g**。
- 手動重置 `lastIndex`:`patt.lastIndex = 0; patt.test(str);` — 每次調用前重置,易忘。
- 用 `String.prototype.match` / `matchAll`**:`"111.12".match(patt)` 不受 `lastIndex` 影響。
最佳實踐
- 驗證輸入 (如表單校驗) **永遠不用 `g`**。
- 需提取所有匹配時用 `matchAll` (ES2020) 或 `exec` 循環並手動管理 `lastIndex`。
- ESLint 規則 `no-regex-spaces` / `require-unicode-regexp` 可輔助檢查。
參考深度解析:RegExp lastIndex 詳解