Vue Front-end Инженер

Vue Front-end Инженер

Роадмап навыков для прокачки

Два способа создать RegExp

Язык JavaScript (ES5)Регулярные выраженияОсновное

Основная идея

В JavaScript есть два способа создания регулярных выражений: литеральная нотация /pattern/flags и конструктор new RegExp(pattern, flags). Литерал проще для статических шаблонов, конструктор — для динамических.

Ключевые аспекты

  • Литерал /pattern/flags — компилируется при загрузке скрипта
  • new RegExp(pattern, flags) — создаётся во время выполнения
  • Экранирование — в конструкторе нужно двойное экранирование \\d
  • Динамический шаблон — только через конструктор

Плюсы и минусы

СпособПлюсыМинусы
ЛитералКороткий синтаксис, нет двойного экранированияТолько статические шаблоны
КонструкторДинамические шаблоныДвойное экранирование

Частые ошибки на собеседованиях

  • Забывают двойное экранирование в конструкторе: new RegExp('\\d+') вместо new RegExp('\d+')
  • Не знают, что литерал компилируется один раз при загрузке
  • Путают порядок аргументов: new RegExp(pattern, flags), не new RegExp(flags, pattern)
  • Забывают, что можно опустить new: RegExp('\\d+') тоже работает

Введение и проблематика

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

Используйте литерал /pattern/ для статических шаблонов и конструктор new RegExp() для динамических.


Базовая теория

Литеральная нотация

js
// Синтаксис: /pattern/flags
var regex1 = /hello/;
var regex2 = /hello/gi;
var regex3 = /\d+/;
 
// Используется, когда шаблон известен заранее

Конструктор RegExp

js
// Синтаксис: new RegExp(pattern, flags)
var regex1 = new RegExp('hello');
var regex2 = new RegExp('hello', 'gi');
var regex3 = new RegExp('\\d+'); // Двойное экранирование!
 
// Можно опустить new
var regex4 = RegExp('hello', 'g');

Code Example 1: В чём разница между двумя способами создания RegExp? Почему в конструкторе нужно двойное экранирование?

Сравнение способов

js
// Простой синтаксис
var regex = /hello world/gi;
 
// Специальные символы — одинарное экранирование
var digits = /\d+/;
var word = /\w+/;
var slash = /\//; // экранируем слеш
ХарактеристикаЛитералКонструктор
Синтаксис/pattern/flagsnew RegExp(pattern, flags)
КомпиляцияПри загрузкеПри выполнении
ЭкранированиеОдинарное \dДвойное \\d
Динамический шаблонНетДа
Слеш в шаблонеНужно экранироватьНе нужно

Практические примеры

Code Example 2: Для каких задач лучше использовать литеральную нотацию? Что проверяют эти регулярные выражения?

Статические шаблоны (литерал)

js
// Для известных заранее шаблонов — литерал проще
var emailRegex = /^\w+@\w+\.\w+$/;
var phoneRegex = /^\d{10,11}$/;
var urlRegex = /^https?:\/\//;
 
// Проверка
emailRegex.test('user@example.com'); // true

Code Example 3: Зачем нужен конструктор new RegExp()? Почему здесь нельзя использовать литерал?

Динамические шаблоны (конструктор)

js
// Поиск введённого пользователем слова
function findWord(text, word) {
  var regex = new RegExp(word, 'gi');
  return text.match(regex);
}
 
findWord('Hello hello HELLO', 'hello');
// ["Hello", "hello", "HELLO"]
 
// Поиск с переменной
var searchTerm = 'JavaScript';
var regex = new RegExp(searchTerm, 'i');
regex.test('I love JavaScript'); // true

Code Example 4: Как эта функция формирует регулярное выражение из массива? Что вернёт validateExtension('photo.JPG', ['jpg', 'png', 'gif'])?

Формирование сложного шаблона

js
// Валидация расширения файла
function validateExtension(filename, allowedExtensions) {
  // allowedExtensions = ['jpg', 'png', 'gif']
  var pattern = '\\.(' + allowedExtensions.join('|') + ')$';
  var regex = new RegExp(pattern, 'i');
  return regex.test(filename);
}
 
validateExtension('photo.JPG', ['jpg', 'png', 'gif']); // true
validateExtension('doc.pdf', ['jpg', 'png', 'gif']);   // false

Экранирование в конструкторе

⚠️

В конструкторе строка сначала интерпретируется JavaScript, затем RegExp. Поэтому нужно двойное экранирование.

Code Example 5: Почему new RegExp('\d+') создаёт не тот паттерн, который ожидается? Как это исправить?

js
// ❌ Неправильно — \d интерпретируется как escape-последовательность
var wrong = new RegExp('\d+');
console.log(wrong); // /d+/ — не то, что хотели!
 
// ✅ Правильно — двойное экранирование
var correct = new RegExp('\\d+');
console.log(correct); // /\d+/
 
// Сравнение
var literal = /\d+/;              // /\d+/
var constructor = new RegExp('\\d+'); // /\d+/ — идентичны

Таблица экранирования

ЛитералКонструкторЗначение
\d\\dЦифра
\w\\wБуква/цифра/_
\s\\sПробельный символ
\n\\nНовая строка
\\\\\\Обратный слеш
\.\\.Точка (буквально)

Пограничные кейсы

Code Example 6: Почему слеш нужно экранировать в литерале, но не нужно в конструкторе?

Слеш в шаблоне

js
// Литерал — слеш нужно экранировать
var urlRegex = /https:\/\//;
 
// Конструктор — слеш не нужно экранировать
var urlRegex2 = new RegExp('https://');
 
// Оба эквивалентны

Code Example 7: Зачем нужна функция escapeRegExp? Что произойдёт без экранирования при поиске '$100'?

Экранирование пользовательского ввода

js
// Если ищем текст с спецсимволами — нужно экранировать
function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
 
var userInput = 'price: $100';
var escaped = escapeRegExp(userInput);
// "price: \\$100"
 
var regex = new RegExp(escaped);
regex.test('The price: $100 is high'); // true

Code Example 8: Как создать копию регулярного выражения с изменёнными флагами? Что делает regex.source?

Создание из другого RegExp

js
var original = /hello/gi;
 
// Создание копии с другими флагами
var modified = new RegExp(original.source, 'i');
 
console.log(original); // /hello/gi
console.log(modified); // /hello/i

Code Example 9: Почему литерал быстрее конструктора? Как оптимизировать использование конструктора?

Производительность

js
// Литерал компилируется один раз при загрузке скрипта
function findWithLiteral(text) {
  return /\d+/g.exec(text); // Быстрее — уже скомпилирован
}
 
// Конструктор создаёт новый RegExp при каждом вызове
function findWithConstructor(text) {
  return new RegExp('\\d+', 'g').exec(text); // Медленнее
}
 
// Оптимизация — создать один раз
var cachedRegex = new RegExp('\\d+', 'g');
function findOptimized(text) {
  cachedRegex.lastIndex = 0;
  return cachedRegex.exec(text);
}

Для статических шаблонов литерал быстрее. Если нужен конструктор — кешируйте созданный RegExp.


Когда что использовать

СитуацияРекомендация
Шаблон известен заранееЛитерал
Шаблон из переменнойКонструктор
Шаблон от пользователяКонструктор + экранирование
Шаблон со слешамиКонструктор (проще)
Критична производительностьЛитерал или кешированный

Вопросы интервьюера

Q: Какие способы создания RegExp вы знаете?

Литеральная нотация /pattern/flags и конструктор new RegExp(pattern, flags).

Q: Когда использовать конструктор?

Когда шаблон формируется динамически — из переменных или пользовательского ввода.

Q: Почему в конструкторе нужно \\d вместо \d?

Строка сначала обрабатывается JavaScript (где \d — просто d), затем RegExp. Двойной слеш \\ превращается в один \.

Q: Как безопасно использовать пользовательский ввод в RegExp?

Экранировать спецсимволы функцией типа escapeRegExp() перед созданием RegExp.


Источники

Code Example 1: Литерал vs Конструктор

❓ В чём разница между двумя способами создания RegExp? Почему в конструкторе нужно двойное экранирование?

Литерал:

js
var regex = /hello world/gi;
 
var digits = /\d+/;
var word = /\w+/;
var slash = /\//;

Конструктор:

js
var regex = new RegExp('hello world', 'gi');
 
var digits = new RegExp('\\d+');
var word = new RegExp('\\w+');
var slash = new RegExp('/');

Code Example 2: Статические шаблоны

❓ Для каких задач лучше использовать литеральную нотацию? Что проверяют эти регулярные выражения?

js
var emailRegex = /^\w+@\w+\.\w+$/;
var phoneRegex = /^\d{10,11}$/;
var urlRegex = /^https?:\/\//;
 
emailRegex.test('user@example.com');

Code Example 3: Динамические шаблоны

❓ Зачем нужен конструктор new RegExp()? Почему здесь нельзя использовать литерал?

js
function findWord(text, word) {
  var regex = new RegExp(word, 'gi');
  return text.match(regex);
}
 
findWord('Hello hello HELLO', 'hello');
 
var searchTerm = 'JavaScript';
var regex = new RegExp(searchTerm, 'i');
regex.test('I love JavaScript');

Code Example 4: Формирование сложного шаблона

❓ Как эта функция формирует регулярное выражение из массива? Что вернёт validateExtension('photo.JPG', ['jpg', 'png', 'gif'])?

js
function validateExtension(filename, allowedExtensions) {
  var pattern = '\\.(' + allowedExtensions.join('|') + ')$';
  var regex = new RegExp(pattern, 'i');
  return regex.test(filename);
}
 
validateExtension('photo.JPG', ['jpg', 'png', 'gif']);
validateExtension('doc.pdf', ['jpg', 'png', 'gif']);

Code Example 5: Двойное экранирование в конструкторе

❓ Почему new RegExp('\d+') создаёт не тот паттерн, который ожидается? Как это исправить?

js
var wrong = new RegExp('\d+');
console.log(wrong);
 
var correct = new RegExp('\\d+');
console.log(correct);
 
var literal = /\d+/;
var constructor = new RegExp('\\d+');

Code Example 6: Слеш в шаблоне

❓ Почему слеш нужно экранировать в литерале, но не нужно в конструкторе?

js
var urlRegex = /https:\/\//;
 
var urlRegex2 = new RegExp('https://');

Code Example 7: Экранирование пользовательского ввода

❓ Зачем нужна функция escapeRegExp? Что произойдёт без экранирования при поиске '$100'?

js
function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
 
var userInput = 'price: $100';
var escaped = escapeRegExp(userInput);
 
var regex = new RegExp(escaped);
regex.test('The price: $100 is high');

Code Example 8: Создание копии RegExp с другими флагами

❓ Как создать копию регулярного выражения с изменёнными флагами? Что делает regex.source?

js
var original = /hello/gi;
 
var modified = new RegExp(original.source, 'i');
 
console.log(original);
console.log(modified);

Code Example 9: Производительность

❓ Почему литерал быстрее конструктора? Как оптимизировать использование конструктора?

js
function findWithLiteral(text) {
  return /\d+/g.exec(text);
}
 
function findWithConstructor(text) {
  return new RegExp('\\d+', 'g').exec(text);
}
 
var cachedRegex = new RegExp('\\d+', 'g');
function findOptimized(text) {
  cachedRegex.lastIndex = 0;
  return cachedRegex.exec(text);
}