Почему я вижу противоречивое поведение логики JavaScript с помощью предупреждения() и без него?

У меня есть код, похожий на эти записи фильтрации в массиве объектов:

var filterRegex = new RegExp(".*blah.*","ig");
if (filterRegex.test(events[i].thing) && events[i].show) {
    console.log("SUCCESS: filtering thing " + i + " " + events[i].thing);
    events[i].show = false;
    numevents--;
}

Я получаю несогласованные результаты с этим условием if (проверка с Firebug, оба условия истинны индивидуально, но иногда все выражение оценивается как false). ОДНАКО, если я на самом деле помещаю alert(), вызываемый внутри этого оператора if (например, строка 4), он становится согласованным, и я получаю результат, который я хочу.

Вы можете увидеть что-то не так с этой логикой и сказать мне, почему он не всегда производит то, что ожидается?

Ответ 1

Хорошо, я вижу это сейчас. Ключом к вашей проблеме является использование флага g (global match): когда это указано для регулярного выражения, оно будет настроено так, что оно может выполняться несколько раз, начиная каждый раз в том месте, где оно осталось в последний раз. Он хранит "закладки" в своем свойстве lastIndex:

var testRegex = /blah/ig;
// logs: true 4
console.log(testRegex.test("blah blah"), testRegex.lastIndex);
// logs: true 9 
console.log(testRegex.test("blah blah"), testRegex.lastIndex);
// logs: false 0
console.log(testRegex.test("blah blah"), testRegex.lastIndex);

В приведенном выше примере создается экземпляр очень простого регулярного выражения: он соответствует "блаху", верхнему или нижнему регистру, где угодно в строке, и его можно сопоставить несколько раз (флаг g). В первом прогоне он соответствует первому "блаху" и оставляет lastIndex равным 4 (индекс пробела после первого "блаха" ). Второй запуск начинается с соответствия lastIndex, соответствует второму блаху и оставляет lastIndex установленным на 9 - один за концом массива. Третий пробег не соответствует - lastIndex является фиктивным - и оставляет lastIndex равным 0. Таким образом, четвертый пробег будет иметь те же результаты, что и первый.

Теперь ваше выражение довольно немного жаднее моего: оно будет соответствовать любому количеству символов до или после "бла". Поэтому, независимо от того, какую строку вы тестируете, если она содержит "blah", она всегда будет соответствовать всей строке и оставьте lastIndex установленной длиной только что протестированной строки. Если вы хотите дважды позвонить test(), второй тест всегда будет терпеть неудачу:

var filterRegex = /.*blah.*/ig;
// logs: true, 9
console.log(filterRegex.test("blah blah"), filterRegex.lastIndex);
// logs: false, 0 
console.log(filterRegex.test("blah blah"), filterRegex.lastIndex);

К счастью, поскольку вы создаете свое регулярное выражение непосредственно перед вызовом test() и никогда не вызываете test() более одного раза, вы никогда не столкнетесь с неожиданным поведением... Если используя отладчик, который позволяет добавить в другой вызов к test() сбоку. Ага. При запуске Firebug выражение watch, содержащее ваш вызов test(), приведет к появлению прерывистых результатов false, либо в вашем коде, либо в результатах просмотра, в зависимости от того, какой из них будет первым. Вождение вас медленно безумие...

Конечно, без флага g, livin 'легко:

var filterRegex = /.*blah.*/i;
// logs: true, 0
console.log(filterRegex.test("blah blah"), filterRegex.lastIndex);
// logs: true, 0 
console.log(filterRegex.test("blah blah"), filterRegex.lastIndex);

Предложения

  • Избегайте глобального флага, когда он вам не нужен.
  • Будьте осторожны с тем, что вы оцениваете в отладчике: если есть побочные эффекты, это может повлиять на поведение вашей программы.

Ответ 2

Я просто не могу себе представить, что существует ситуация, когда два выражения JavaScript оцениваются как true, но не при объединении.

Вы уверены, что оба выражения на самом деле производят логическое значение каждый раз? (Хорошо, чтобы сделать regex.test() не создавать логическое значение, сложно, но как насчет event.show. Может ли быть undefined время от времени?

Вы ссылаетесь на правильный индекс, говоря event[0].show, не означает ли вы event[i].show?

Ответ 3

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