Как стать автором
Обновить

Коварный regex.exec

Натолкнулся сегодня на «грабли», достойные, как мне кажется, чтобы написать о них мини пост.

var myRegex = /abc/gi;
var myString = "hello abc";

var a = myRegex.exec(myString);
var b = myRegex.exec(myString);

Казалось бы, a и b должны содержать одинаковый результат, но нет.



Результат будет таков:

a = ["abc"];
b = null;


В объекте myRegex содержится очень хитрое свойство lastIndex, которое запоминает, в какой позиции закончился последний матч и в следующий раз начинает искать с этой позиции.

var a = myRegex.exec(myString);
alert(myRegex.lastIndex); // 9

var b = myRegex.exec(myString);
alert(myRegex.lastIndex); // 0


Так получается из-за того, что регулярное выражение определено с флагом g – global, который заставляет выражение ходить по всем матчам, а не только брать первый попавшийся.

Что будет если работать с двумя разными строками?

Ничего не изменится, поскольку флаг этот хранится в объекте регулярного выражения а не для строки.
var myString1 = "don't say hello to abc";
var myString2 = "say hello to abc";

var a = myRegex.exec(myString1);
var b = myRegex.exec(myString2);

a // ["abc"];
b // null;


Богатая почва для фейлов.

Вообще говоря, это совершенно нормальное поведение, которое, тем не менее, не совсем очевидно.
Можно внутри цикла каждый раз создавать новый объект регулярного выражения, потом для увеличения скорости решить вынести его определение перед циклом и отгрести.

OK:
for (var i = 0; i < list.length; i++)
{
    var myRegex = /abc/gi;
    
    ...   myRegex.exec(...) ...
}


Бага:
var myRegex = /abc/gi;

for (var i = 0; i < list.length; i++)
{    
    ...   myRegex.exec(...) ...
}


Поэтому нужно либо не использовать флаг g без надобности, либо не забывать перед каждым использованием выражения выставлять myRegex.lastIndex = 0;
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.