Avatar ·

Как использовать переменную в регулярном выражении в JavaScript?

Привет всем! Я новичок в JavaScript и пытаюсь написать простую функцию поиска. У меня есть массив строк, и я хочу найти все элементы, которые содержат определённое слово. Слово для поиска передаётся как аргумент функции. Проблема в том, что я не могу понять, как вставить переменную внутрь регулярного выражения. Когда я пишу так: ```javascript function searchItems(array, searchTerm) { const regex = /searchTerm/gi; return array.filter(item => regex.test(item)); } ``` ...то оно ищет буквально строку "searchTerm", а не значение переменной. Я пробовал использовать `new RegExp()`, но у меня всё равно что-то не получается. Вот мой текущий код: ```javascript function searchItems(array, searchTerm) { const regex = new RegExp(searchTerm, 'gi'); return array.filter(item => regex.test(item)); } const fruits = ['Apple', 'Banana', 'Avocado', 'Grape']; console.log(searchItems(fruits, 'ap')); // Ожидаю: ['Apple', 'Avocado'] ``` Он вроде работает, но я заметил странное поведение: если я вызываю функцию несколько раз, результаты иногда меняются. И ещё я не знаю, как экранировать специальные символы вроде точки или звёздочки, если пользователь введёт их в поисковый запрос. Подскажите, как правильно работать с переменными в регулярных выражениях? Заранее спасибо! <<>>

Основное решение: конструктор RegExp и экранирование спецсимволов

Вы на правильном пути — new RegExp(searchTerm, 'gi') — это верный способ использовать переменную в регулярном выражении. Однако есть две важные детали, которые нужно учесть.

Почему результаты "меняются"?

Проблема в методе regex.test(). У регулярных выражений с флагом g (глобальный поиск) есть свойство lastIndex. После каждого успешного совпадения test() запоминает позицию и начинает следующий поиск с неё. При повторном вызове на том же экземпляре RegExp результаты могут быть неожиданными.

Решение: либо создавайте новый RegExp для каждого вызова, либо сбрасывайте lastIndex в ноль:

function searchItems(array, searchTerm) {
  const regex = new RegExp(searchTerm, 'gi');
  return array.filter(item => {
    regex.lastIndex = 0; // Сбрасываем позицию
    return regex.test(item);
  });
}

Экранирование специальных символов

Если пользователь введёт ., *, +, ?, (, ), [, ] и другие спецсимволы, они будут интерпретироваться как часть синтаксиса регулярных выражений. Чтобы искать именно эти символы как обычный текст, их нужно экранировать обратной косой чертой.

Вот функция для экранирования:

function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

function searchItems(array, searchTerm) {
  const escapedTerm = escapeRegExp(searchTerm);
  const regex = new RegExp(escapedTerm, 'gi');
  return array.filter(item => regex.test(item));
}

Полный рабочий пример

function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

function searchItems(array, searchTerm) {
  if (!searchTerm) return array; // Пустой поиск возвращает всё
  const escapedTerm = escapeRegExp(searchTerm);
  const regex = new RegExp(escapedTerm, 'gi');
  return array.filter(item => regex.test(item));
}

const fruits = ['Apple', 'Banana', 'Avocado', 'Grape'];
console.log(searchItems(fruits, 'ap')); // ['Apple', 'Avocado']
console.log(searchItems(fruits, '.'));  // ['Apple', 'Banana', 'Avocado', 'Grape'] — без экранирования нашло бы всё

Альтернативный подход: String.prototype.includes()

Если вам не нужны возможности регулярных выражений (регистронезависимость, сложные шаблоны), можно использовать более простой метод:

function searchItems(array, searchTerm) {
  const lowerTerm = searchTerm.toLowerCase();
  return array.filter(item => item.toLowerCase().includes(lowerTerm));
}

Этот подход безопаснее и быстрее для простого поиска подстроки.

Краткий чек-лист:

  • Используйте new RegExp(variable, flags) для динамических шаблонов
  • Всегда экранируйте пользовательский ввод функцией escapeRegExp()
  • Сбрасывайте lastIndex или создавайте новый RegExp при повторных вызовах с флагом g
  • Для простого поиска подстроки без спецсимволов используйте includes() — проще и надёжнее
<<>>

Использование литералов шаблонов с eval() (не рекомендуется)

Технически можно создать регулярное выражение из строки с помощью eval():

const searchTerm = 'ap';
const regex = eval(`/${searchTerm}/gi`);

НО: я настоятельно не рекомендую этот подход. eval() выполняет произвольный JavaScript-код, что создаёт серьёзные уязвимости безопасности, особенно если переменная содержит пользовательский ввод. Злоумышленник может передать что-то вроде "; alert('XSS'); // и выполнить вредоносный код.

Безопасная альтернатива с использованием флагов

Если вам нужно комбинировать несколько флагов в зависимости от условий, используйте конструктор RegExp:

function createRegex(pattern, caseSensitive = false, global = true) {
  let flags = '';
  if (!caseSensitive) flags += 'i';
  if (global) flags += 'g';
  return new RegExp(pattern, flags);
}

// Пример использования
const regex1 = createRegex('apple', false, true); // /apple/gi
const regex2 = createRegex('apple', true, false); // /apple/

Этот подход даёт гибкость в настройке флагов без риска, связанного с eval(). Для экранирования спецсимволов используйте функцию из первого ответа.