Интересный тест 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