Выразительный JavaScript (Хавербеке) - страница 98

заменяется текстом, который совпал с первой группой, >$2 – со второй группой, и так далее, до >$9. Всё совпадение целиком содержится в переменной >$&.

Также можно в качестве второго аргумента передавать и функцию. Для каждой замены будет вызвана функция, аргументами которой будут найденные группы (и вся совпадающая часть строки целиком), а её результат будет вставлен в новую строку.

Простой пример:

>var s = "the cia and fbi";

>console.log(s.replace(/\b(fbi|cia)\b/g, function(str) {

>  return str.toUpperCase();

>}));

>// → the CIA and FBI

А вот более интересный:

>var stock = "1 lemon, 2 cabbages, and 101 eggs";

>function minusOne(match, amount, unit) {

>  amount = Number(amount) - 1;

>  if (amount == 1) // остался только один, удаляем 's' в конце

>    unit = unit.slice(0, unit.length - 1);

>  else if (amount == 0)

>    amount = "no";

>  return amount + " " + unit;

>}

>console.log(stock.replace(/(\d+) (\w+)/g, minusOne));

>// → no lemon, 1 cabbage, and 100 eggs

Код принимает строку, находит все вхождения чисел, за которыми идёт слово, и возвращает строчку, где каждое число уменьшено на единицу.

Группа >(\d+) попадает в аргумент >amount, а >(\w+) – в >unit. Функция преобразовывает >amount в число – и это всегда срабатывает, потому что наш шаблон как раз >\d+. И затем вносит изменения в слово, на случай, если остался всего один предмет или ни одного.

Жадность

Несложно при помощи >replace написать функцию, убирающую все комментарии из кода JavaScript. Вот первая попытка:

>function stripComments(code) {

>  return code.replace(/\/\/.*|\/\*[^]*\*\//g, "");

>}

>console.log(stripComments("1 + /* 2 */3"));

>// → 1 + 3

>console.log(stripComments("x = 10;// ten!"));

>// → x = 10;

>console.log(stripComments("1 /* a */+/* b */ 1"));

>// → 1  1

Часть перед оператором «или» совпадает с двумя слэшами, за которыми идёт любое количество символов, кроме символов перевода строки. Часть, убирающая многострочные комментарии, более сложна. Мы используем , т. е. любой символ, не являющийся пустым, в качестве способа найти любой символ. Мы не можем использовать точку, потому что блочные комментарии продолжаются и на новой строке, а символ перевода строки не совпадает с точкой.

Но вывод предыдущего примера неправильный. Почему?

Часть сначала попытается захватить столько символов, сколько может. Если из-за этого следующая часть регулярки не найдёт себе совпадения, произойдёт откат на один символ и попробует снова. В примере, алгоритм пытается захватить всю строку, и затем откатывается. Откатившись на четыре символа назад, он найдёт в строчке >/ — а это не то, чего мы добивались. Мы-то хотели захватить только один комментарий, а не пройти до конца строки и найти последний комментарий.