Марейн Хавербеке - Выразительный JavaScript
- Название:Выразительный JavaScript
- Автор:
- Жанр:
- Издательство:неизвестно
- Год:неизвестен
- ISBN:978-1593275846
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Марейн Хавербеке - Выразительный JavaScript краткое содержание
В процессе чтения вы познакомитесь с основами программирования и, в частности, языка JavaScript, а также выполните несколько небольших проектов. Один из самых интересных проектов — создание своего языка программирования.
Выразительный JavaScript - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
console.log("папа".replace("п", "м"));
// → мапа
Первый аргумент может быть и регуляркой, в каком случае заменяется первое вхождение регулярки в строке. Когда к регулярке добавляется опция g
(global, всеобщий), заменяются все вхождения, а не только первое.
console.log("Borobudur".replace(/[ou]/, "a"));
// → Barobudur
console.log("Borobudur".replace(/[ou]/g, "a"));
// → Barabadar
Имело бы смысл передавать опцию «заменить все» через отдельный аргумент, или через отдельный метод типа replaceAll
. Но к сожалению, опция передаётся через саму регулярку.
Вся сила регулярок раскрывается, когда мы используем ссылки на найденные в строке группы, заданные в регулярке. Например, у нас есть строка, содержащая имена людей, одно имя на строчку, в формате « Фамилия
, Имя
». Если нам надо поменять их местами и убрать запятую, чтобы получилось « Имя
Фамилия
», мы пишем следующее:
console.log(
"Hopper, Grace\nMcCarthy, John\nRitchie, Dennis"
.replace(/([\w ]+), ([\w ]+)/g, "$2 $1"));
// → Grace Hopper
// John McCarthy
// Dennis Ritchie
$1
и $2
в строчке на замену ссылаются на группы символов, заключённые в скобки. $1
заменяется текстом, который совпал с первой группой, $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
Часть перед оператором «или» совпадает с двумя слэшами, за которыми идёт любое количество символов, кроме символов перевода строки. Часть, убирающая многострочные комментарии, более сложна. Мы используем , т. е. любой символ, не являющийся пустым, в качестве способа найти любой символ. Мы не можем использовать точку, потому что блочные комментарии продолжаются и на новой строке, а символ перевода строки не совпадает с точкой.
Но вывод предыдущего примера неправильный. Почему?
Часть сначала попытается захватить столько символов, сколько может. Если из-за этого следующая часть регулярки не найдёт себе совпадения, произойдёт откат на один символ и попробует снова. В примере, алгоритм пытается захватить всю строку, и затем откатывается. Откатившись на четыре символа назад, он найдёт в строчке /
— а это не то, чего мы добивались. Мы-то хотели захватить только один комментарий, а не пройти до конца строки и найти последний комментарий.
Из-за этого мы говорим, что операторы повторения ( +
, *
, ?
, and {}
) жадные, то есть они сначала захватывают, сколько могут, а потом идут назад. Если вы поместите вопрос после такого оператора ( +?
, *?
, ??
, {}?
), они превратятся в нежадных, и начнут находить самые маленькие из возможных вхождений.
И это то, что нам нужно. Заставив звёздочку находить совпадения в минимально возможном количестве символов строчки, мы поглощаем только один блок комментариев, и не более того.
function stripComments(code) {
return code.replace(/\/\/.*|\/\*[^]*?\*\//g, "");
}
console.log(stripComments("1 /* a */+/* b */ 1"));
// → 1 + 1
Множество ошибок возникает при использовании жадных операторов вместо нежадных. При использовании оператора повтора сначала всегда рассматривайте вариант нежадного оператора.
Динамическое создание объектов RegExp
В некоторых случаях точный шаблон неизвестен во время написания кода. Например, вам надо будет искать имя пользователя в тексте, и заключать его в подчёркивания. Так как вы узнаете имя только после запуска программы, вы не можете использовать запись со слэшами.
Но вы можете построить строку и использовать конструктор RegExp
. Вот пример:
var name = "гарри";
var text = "А у Гарри на лбу шрам.";
var regexp = new RegExp("\\b(" + name + ")\\b", "gi");
console.log(text.replace(regexp, "_$1_"));
// → А у _Гарри_ на лбу шрам.
При создании границ слова приходится использовать двойные слэши, потому что мы пишем их в нормальной строке, а не в регулярке с прямыми слэшами. Второй аргумент для RegExp
содержит опции для регулярок – в нашем случае “gi”
, т. е. глобальный и регистронезависимый.
Но что, если имя будет "dea+hl[]rd"
(если наш пользователь – кульхацкер)? В результате мы получим бессмысленную регулярку, которая не найдёт в строке совпадений.
Мы можем добавить обратных слэшей перед любым символом, который нам не нравится. Мы не можем добавлять обратные слэши перед буквами, потому что \b
или \n
– это спецсимволы. Но добавлять слэши перед любыми не алфавитно-цифровыми символами можно без проблем.
var name = "dea+hl[]rd";
var text = "Этот dea+hl[]rd всех достал.";
var escaped = name.replace(/[^\w\s]/g, "\\$&");
var regexp = new RegExp("\\b(" + escaped + ")\\b", "gi");
console.log(text.replace(regexp, "_$1_"));
// → Этот _dea+hl[]rd_ всех достал.
Метод search
Метод indexOf
нельзя использовать с регулярками. Зато есть метод search
, который как раз ожидает регулярку. Как и indexOf
, он возвращает индекс первого вхождения, или -1, если его не случилось.
console.log(" word".search(/\S/));
// → 2
console.log(" ".search(/\S/));
// → -1
К сожалению, никак нельзя задать, чтобы метод искал совпадение, начиная с конкретного смещения (как это можно сделать с indexOf
). Это было бы полезно.
Интервал:
Закладка: