Как использовать переменную в регулярном выражении в JavaScript?
Основное решение: конструктор 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(). Для экранирования спецсимволов используйте функцию из первого ответа.