Подтвердить что ты не робот

Почему же RegExp ведет себя по-другому?

Возможный дубликат:
Интересный тест Javascript RegExp
Тест регулярного выражения не может решить между true и false (JavaScript)

Пример проблемы. При запуске inline результаты будут такими, как я ожидал. Но когда он хранится как переменная, он пропускает элемент среднего диапазона.

// Inline RegExp
function getToggleClasses() {
  var toggler = [],
      elements = document.getElementsByTagName("*"),
      i=0,
      len = elements.length;

  for (i; i < len; i++) {
    if (/toggler/g.test(elements[i].className)) {
      toggler.push(elements[i]);
    }
  }

  document.getElementById('results').innerHTML += "<br />Inline: " + toggler.length;
}

// Variable
function getToggleClasses2() {
  var toggler = [],
      elements = document.getElementsByTagName("*"),
      tester = /toggler/g,
      i=0,
      len = elements.length;

  for (i; i < len; i++) {
    if (tester.test(elements[i].className)) {
      toggler.push(elements[i]);
    }
  }

  document.getElementById('results').innerHTML += "<br />Variable: " + toggler.length;
}
​

Отметьте:

<span class="toggler">A</span>
<span class="toggler">B</span>
<span class="toggler">C</span>

Учитывая: Я понимаю, что нет смысла использовать RegExp для сравнения, и я также понимаю, насколько велики такие библиотеки, как jQuery. Я также знаю, что g в этом случае не требуется.

Я не могу понять, почему эти два метода должны возвращать разные результаты.

4b9b3361

Ответ 1

RegExp экземпляры имеют состояние, поэтому их повторное использование может привести к неожиданному поведению. В этом конкретном случае это потому, что экземпляр global, что означает:

чтобы регулярное выражение проверялось на все возможные совпадения в строке.

Однако не единственная разница, вызванная использованием g. От RegExp.test @MDN:

Как и в случае с exec (или в сочетании с ним), test, вызываемый несколько раз в одном и том же экземпляре глобального регулярного выражения, будет проходить мимо предыдущего совпадения.


Удалить флаг g или установите lastIndex в 0 (спасибо, @zzzzBov).

Ответ 2

/g не требуется и не должен использоваться в этом случае.

Поведение отличается в этих случаях, потому что в "inline" случае объект regex воссоздается каждой итерацией цикла. Хотя в переменной создается один раз и сохраняет свое состояние (lastIndex) между итерациями цикла.

Переместите var в цикл, и вы получите тот же результат:

// Variable
function getToggleClasses2() {
  var toggler = [],
      elements = document.getElementsByTagName("*"),
      i=0,
      len = elements.length;

  for (i; i < len; i++) {
    var tester = /toggler/g;
    if (tester.test(elements[i].className)) {
      toggler.push(elements[i]);
    }
  }

  document.getElementById('results').innerHTML += "<br />Variable: " + toggler.length;
}

Ответ 3

Регулярное выражение поддерживает переменную с именем lastIndex, которая является индексом для запуска следующего поиска. Из MDN:

Как и в случае с exec (или в сочетании с ним), test, вызываемый несколько раз в одном и том же экземпляре глобального регулярного выражения, будет проходить мимо предыдущего совпадения.

Когда вы определяете встроенное регулярное выражение для каждой итерации, состояние теряется и lastIndex всегда равно 0, потому что вы каждый раз используете новое регулярное выражение. Если вы сохраняете регулярное выражение в достоверном значении, lastIndex сохраняется как конечная позиция последнего совпадения, что в этом случае вызывает следующий поиск для начала в конце следующей строки, что приводит к неудачному совпадению. Когда происходит третье сравнение, lastIndex был reset равным 0, потому что регулярное выражение знает, что в последний раз оно не получило результатов.