Интересный тест Javascript RegExp
Я написал тест Javascript RegExp для определения формата строки даты, по ошибке добавил избыточный флаг "g" и нашел что-то интересное.
var s = "2009/03/10";
var regex=/^\d{4}[/]\d{2}[/]\d{2}$/g;
alert(regex.test(s));
alert(regex.test(s));
alert(regex.test(s));
alert(regex.test(s));
Я получил "true", за которым следует "false", затем еще одно "true", затем еще одно "false".
Если я использую цикл для его выполнения, я найду что-то более интересное, я получу четыре "true" в IE и Safari и true, false, true, false в FF, Chrome.
for (var i=0; i<4; i++)
{
var s = "2009/03/10";
var regex=/^\d{4}[/]\d{2}[/]\d{2}$/g;
alert(regex.test(s));
}
У кого-нибудь есть идея объяснить, почему регулярное выражение Javascript ведет себя так и почему браузеры возвращают разные результаты? (связано с объявлением переменной и областью действия?)
Ответы
Ответ 1
Когда вы используете глобальный флаг в JS RegExp, методы "test" и "exec" останавливаются в первом совпадении, но сохраняют указатель на место, где они прекратили поиск в строке. Этот указатель можно проверить в свойстве lastIndex
. Когда вы снова вызываете "тест" или "exec", он начинает поиск соответствия, начинающегося с lastIndex
.
Итак, когда вы проверяете RegExp на строку, которая соответствует всей строке, lastIndex устанавливается в конец строки. При следующем тестировании он начинается в конце строки, возвращает false
и устанавливает lastIndex
обратно на ноль.
В MDC есть достойное объяснение этого поведения.
Ответ 2
Чтобы избежать этого странного поведения, не используйте глобальный флаг (g).
Этот код должен выводить: "True", "True", "True", "True"
var s = "2009/03/10";
var regex=/^\d{4}[/]\d{2}[/]\d{2}$/i;
alert(regex.test(s));
alert(regex.test(s));
alert(regex.test(s));
alert(regex.test(s));
Глобальная (g) квартира задает свойство RegExp.lastIndex. Поэтому каждый тест() будет начинаться с остановки последнего.
Для получения дополнительной информации см. документацию о RegExp.lastIndex