Когда одна и та же регулярка используется в нескольких местах, велик соблазн вынести её в отдельную переменную и избежать дублирования. Чтобы код после этого неожиданно не сломался, важно знать о неочевидной особенности регулярных выражений с флагом g
в JavaScript.
Рассмотрим пример:
const digits = /\d+/;
digits.test("123");
// -> true
digits.test("456");
// -> true
digits.test("789");
// -> true
Всё работает ожидаемо. А теперь добавим флаг g
, с которым регулярка должна искать все совпадения:
const digits = /\d+/g;
digits.test("123");
// -> true
digits.test("456");
// -> false
digits.test("789");
// -> true
Теперь проверка срабатывает через раз. Дело в том, что флаг g
включает сохранение состояния поиска в поле lastIndex
, регулярка становится stateful, а не stateless. При вызове методов test
или exec
поиск начинается с позиции lastIndex
. При отсутствии совпадений lastIndex
обнуляется, как президентские сроки, а при обнаружении совпадения в lastIndex
записывается позиция после совпадения.
Чтобы избежать этой проблемы, lastIndex
можно перезаписывать вручную:
const digits = /\d+/g;
digits.test("123");
// -> true
digits.lastIndex = 0;
digits.test("456");
// -> true
digits.lastIndex = 0;
digits.test("789");
// -> true
К сожалению, это не самое изящное решение, оно добавляет когнитивной нагрузки — нужно помнить о необходимости сброса индекса при каждом поиске. Можно намекать на природу регулярки префиксом stateful
или global
в названии переменной, чтобы при использовании не забывали обнулять lastIndex
; можно писать и использовать собственные функции-обёртки, которые будут обнулять lastIndex
при каждом вызове; а можно продолжать хардкодить регулярки в местах их использования — выбор за вами.