Последовательные вызовы теста RegExp для шаблона с глобальной опцией

Я боролся с этим весь день, и я не могу понять, что я делаю что-то неправильно или я обнаружил ошибку в движке JavaScript Chrome. Похоже, что последовательные вызовы объекта RegExp с глобальным флагом возвращают несогласованные результаты для одной и той же входной строки. Я тестирую следующую функцию:

function testRegex(pattern, array) {
    document.writeln('Pattern = ' + pattern + ', Array = ' + array + '<br/>');
    for (var ii = 0; ii < array.length; ii++) {
        document.writeln(ii + ', ');
        document.writeln(array[ii] + ', ');
        document.writeln(pattern.test(array[ii]) + '<br />');
    }
    document.writeln('<br/>');
}

Когда я вызываю функцию с /a/g как шаблон и различные массивы строк, я получаю следующие результаты, многие из которых являются неправильными, насколько я могу сказать:

// EXPECTED: True
// ACTUAL:   True
testRegex(/a/g, ['a']);

// EXPECTED: True,  True
// ACTUAL:   True,  False 
testRegex(/a/g, ['a', 'a']);

// EXPECTED: True, True,  True
// ACTUAL:   True, False, True
testRegex(/a/g, ['a', 'a', 'a']);

// EXPECTED: True, False, True
// ACTUAL:   True, False, True
testRegex(/a/g, ['a', 'b', 'a']);

// EXPECTED: True, True,  True, True
// ACTUAL:   True, False, True, False
testRegex(/a/g, ['a', 'a', 'a', 'a']);

// EXPECTED: True, False, False, True
// ACTUAL:   True, False, False, True   
testRegex(/a/g, ['a', 'b', 'b', 'a']);

Когда я вызываю ту же функцию с теми же массивами строк, но передаю /a/ в качестве шаблона, фактические результаты все соответствуют ожидаемым результатам.

// EXPECTED: True
// ACTUAL:   True
testRegex(/a/, ['a']);

// EXPECTED: True, True
// ACTUAL:   True, True
testRegex(/a/, ['a', 'a']);

// EXPECTED: True, True, True
// ACTUAL:   True, True, True
testRegex(/a/, ['a', 'a', 'a']);

// EXPECTED: True, False, True
// ACTUAL:   True, False, True
testRegex(/a/, ['a', 'b', 'a']);

// EXPECTED: True, True, True, True
// ACTUAL:   True, True, True, True
testRegex(/a/, ['a', 'a', 'a', 'a']);

// EXPECTED: True, False, False, True
// ACTUAL:   True, False, False, True
testRegex(/a/, ['a', 'b', 'b', 'a']);

Я создал рабочий пример вышеприведенного кода: http://jsfiddle.net/FishBasketGordo/gBWsN/

Я что-то упустил? Разве результаты не должны быть одинаковыми для заданных массивов строк независимо от того, является ли шаблон глобальной или нет? Заметьте, что я работал в основном в Chrome, но я наблюдал аналогичные неверные результаты в Firefox 4 и IE 8.

Ответ 1

Если вы меняете тестовый цикл следующим образом:

for (var ii = 0; ii < array.length; ii++) {
    document.writeln(ii + ', ');
    document.writeln(array[ii] + ', ');
    document.writeln(pattern.test(array[ii]) + '<br />');
    pattern.lastIndex = 0;
}

Тогда ваш код будет работать. Проблема в том, что флаг "g" вызывает зависание объекта RegExp. Значение "lastIndex" установлено после 1-й итерации этого цикла из-за "g" . Если вы не вернетесь к поиску reset, тогда он предполагает, что при втором вызове вы просите его продолжать перемещаться со смещения 1.

Использование флага "g" в регулярном выражении вне контекста вызова ".replace()" имеет нечетные семантические последствия в любом случае.

Ответ 2

Это не ошибка, а функция. Полученные вами результаты не являются "неправильными", только неожиданными.

10.3.2. Свойства экземпляра RegExp

Каждый объект RegExp имеет пять свойств. Свойство source - это строка только для чтения, содержащая текст регулярного выражения. Глобальное свойство является логическим значением, доступным только для чтения, которое указывает, имеет ли регулярное выражение флаг g. Свойство ignoreCase является логическим значением, доступным только для чтения, которое указывает, имеет ли регулярное выражение флаг i. Многолистное свойство является логическим значением, доступным только для чтения, которое указывает, имеет ли регулярное выражение флаг m. Конечным свойством является lastIndex, целое число чтения-записи. Для шаблонов с флагом g это свойство сохраняет позицию в строке , в которой начнется следующий поиск. Он используется методами exec() и test(), как описано в предыдущем разделе.

Источник