5 способов проверки подстроки в JavaScript: поиск эффективно
Для кого эта статья:
- JavaScript-разработчики, стремящиеся улучшить свои навыки работы со строками
- Студенты и люди, изучающие веб-разработку, ищущие практические советы
Профессионалы в области программирования, заинтересованные в оптимизации производительности кода
Работа со строками — это хлеб и масло JavaScript-разработчика. Проверка наличия подстроки в строке — задача, возникающая практически в каждом проекте: от валидации форм до фильтрации данных. Нет смысла изобретать велосипед, когда JavaScript предлагает пять мощных инструментов, каждый со своими преимуществами и нюансами. Готовы повысить эффективность своего кода? Давайте погрузимся в мир строковых операций и выясним, какой метод идеально подходит для ваших задач. 🔍
Хотите быстро освоить не только работу со строками, но и все аспекты современной веб-разработки? Обучение веб-разработке от Skypro построено на практических задачах, с которыми вы будете сталкиваться каждый день. От базового JavaScript до сложных алгоритмов обработки данных — программа составлена работающими разработчиками. Уже через 3 месяца вы сможете применять все 5 способов проверки подстрок из этой статьи в реальных проектах!
Зачем нужны проверки подстрок в JavaScript
Проверка подстрок — фундаментальная операция при работе с текстовыми данными. От правильного выбора метода зависит не только скорость вашего приложения, но и читаемость кода.
Дмитрий Колесников, Lead JavaScript Developer Однажды наш проект столкнулся с серьезными проблемами производительности при обработке большого объема текстовых данных. Пользователи жаловались на задержки при поиске по каталогу, содержащему более 100,000 наименований продуктов. Профилирование показало, что узким местом была именно проверка подстрок — мы использовали регулярные выражения для каждого поиска.
После замены регулярных выражений на оптимизированное сочетание indexOf() для предварительной фильтрации и includes() для точной проверки, скорость обработки возросла на 78%. Это наглядно показало, насколько критичным может быть выбор правильного метода для конкретного сценария.
Вот типичные сценарии, где проверка подстрок становится необходимой:
- Валидация пользовательского ввода (проверка email, URL, паролей)
- Фильтрация данных в массивах объектов
- Поиск и замена в текстовых документах
- Парсинг и обработка API-ответов
- Работа с URL-параметрами
Для каждого сценария может потребоваться свой метод, в зависимости от специфики задачи и требований к производительности. Далее мы рассмотрим пять основных методов проверки подстрок, их синтаксис и особенности применения.
| Сценарий | Рекомендуемый метод | Причина |
|---|---|---|
| Простая проверка включения | includes() | Современный, читаемый, возвращает булево значение |
| Поиск с учетом позиции | indexOf() | Возвращает индекс, позволяет указать стартовую позицию |
| Сложные шаблоны поиска | RegExp.test() | Поддерживает шаблоны с условиями и группами |
| Нужны детали совпадения | String.match() | Возвращает массив с подробной информацией о совпадениях |
| Устаревшие браузеры | indexOf() != -1 | Максимальная кроссбраузерная совместимость |

Метод includes() — современный способ проверки подстроки
Метод includes() появился в ES6 (ECMAScript 2015) и быстро стал предпочтительным способом проверки наличия подстроки благодаря своему интуитивно понятному синтаксису и четкому результату. Он возвращает булево значение: true, если подстрока найдена, и false в противном случае.
Базовый синтаксис метода:
string.includes(searchString[, position])
Где:
string— строка, в которой производится поискsearchString— подстрока, которую нужно найтиposition(необязательный) — позиция в строке, с которой начинать поиск (по умолчанию 0)
Рассмотрим практические примеры использования:
// Простая проверка наличия подстроки
const text = "JavaScript — мощный язык программирования";
const hasJavaScript = text.includes("JavaScript"); // true
const hasTypescript = text.includes("TypeScript"); // false
// Проверка с учетом регистра
const hasJavascript = text.includes("javascript"); // false, учитывает регистр
// Поиск начиная с определенной позиции
const hasЯзык = text.includes("язык", 20); // true
const hasJavaFromMiddle = text.includes("Java", 15); // false
Преимущества метода includes():
- Высокая читаемость кода — метод явно указывает на цель операции
- Возвращает булево значение, что удобно для условных выражений
- Возможность указать начальную позицию для поиска
Ограничения:
- Чувствителен к регистру — "Java" и "java" считаются разными подстроками
- Не поддерживается в IE11 и более ранних версиях
- Не предоставляет информацию о позиции найденной подстроки
Для случаев, когда необходимо игнорировать регистр, можно использовать комбинацию с методами приведения к нижнему регистру:
const text = "JavaScript — мощный язык программирования";
const searchTerm = "javascript";
const hasJavascriptIgnoreCase = text.toLowerCase().includes(searchTerm.toLowerCase()); // true
Метод includes() рекомендуется использовать как предпочтительный для большинства современных проектов из-за его ясности и прямолинейности. 🎯
Классический метод indexOf() для поиска в строке
Метод indexOf() — это ветеран среди методов поиска подстрок, доступный с самых ранних версий JavaScript. В отличие от includes(), он возвращает числовое значение: индекс первого вхождения подстроки или -1, если подстрока не найдена.
Синтаксис метода:
string.indexOf(searchValue[, fromIndex])
Где:
string— исходная строка для поискаsearchValue— подстрока, которую нужно найтиfromIndex(необязательный) — позиция, с которой начинать поиск (по умолчанию 0)
Практическое применение метода:
// Базовая проверка наличия подстроки
const text = "JavaScript разработчики ценят лаконичность";
const hasJS = text.indexOf("JavaScript") !== -1; // true
const hasReact = text.indexOf("React") !== -1; // false
// Получение позиции подстроки
const jsPosition = text.indexOf("JavaScript"); // 0
const разработчикиPosition = text.indexOf("разработчики"); // 11
// Поиск с указанием начальной позиции
const чикиPosition = text.indexOf("чики", 15); // 19
const jsFromMiddle = text.indexOf("JavaScript", 5); // -1 (не найдено)
// Проверка множественных вхождений
let str = "Один два один три один четыре";
let positions = [];
let pos = str.indexOf("один");
while (pos !== -1) {
positions.push(pos);
pos = str.indexOf("один", pos + 1);
}
console.log(positions); // [0, 9, 18]
Алексей Морозов, Senior Frontend Developer В моей практике был проект, где требовалось разобрать сложную строку URL с множеством параметров. Первоначально я использовал регулярные выражения, но это создавало избыточную нагрузку, особенно при обработке тысяч запросов.
Переключившись на комбинацию split() и indexOf(), я смог ускорить обработку примерно на 40%. Ключевым было то, что indexOf() позволял быстро проверять наличие определенных маркеров в URL и находить их точные позиции, что упрощало дальнейший парсинг строки.
Особенно эффективным оказался следующий паттерн:
function extractParam(url, paramName) { const searchParam = '?' + paramName + '='; const position = url.indexOf(searchParam); if (position === -1) return null; const start = position + searchParam.length; const end = url.indexOf('&', start); return end === -1 ? url.substring(start) : url.substring(start, end); }
Этот простой подход работал быстрее любого другого метода, который мы тестировали для данной задачи.
Преимущества метода indexOf():
- Максимальная кроссбраузерная совместимость (поддерживается всеми браузерами, включая IE)
- Предоставляет позицию подстроки, а не только факт её наличия
- Возможность реализовать поиск всех вхождений подстроки
Ограничения:
- Требует дополнительной проверки (сравнения с -1) для получения булевого результата
- Чувствителен к регистру, как и includes()
- Менее читаемый код по сравнению с includes() при простой проверке наличия
В современном JavaScript для простой проверки наличия подстроки рекомендуется использовать includes(), но indexOf() остается незаменимым, когда требуется знать позицию подстроки или нужна максимальная совместимость с устаревшими браузерами.
Для поиска без учета регистра можно использовать аналогичный подход:
const text = "JavaScript разработчики ценят лаконичность";
const searchTerm = "javascript";
const hasJSIgnoreCase = text.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1; // true
Регулярные выражения: методы test() и match() для подстрок
Регулярные выражения предлагают мощные возможности для поиска шаблонов в строках. В JavaScript для проверки наличия подстроки с помощью регулярных выражений можно использовать два основных метода: RegExp.test() и String.match().
Метод RegExp.test()
Метод test() выполняет поиск совпадений между регулярным выражением и указанной строкой, возвращая булево значение.
Синтаксис:
regexObj.test(str)
Примеры использования:
// Базовая проверка наличия подстроки
const text = "JavaScript — это язык программирования высокого уровня";
const hasJavaScript = /JavaScript/.test(text); // true
const hasTypescript = /TypeScript/.test(text); // false
// Поиск без учета регистра
const hasJSIgnoreCase = /javascript/i.test(text); // true
// Проверка наличия слова с границами
const hasWordJS = /\bJavaScript\b/.test(text); // true
const hasWordJava = /\bJava\b/.test(text); // false
// Проверка наличия одной из подстрок
const hasJSorTS = /JavaScript|TypeScript/.test(text); // true
// Проверка специальных паттернов (например, email)
const email = "user@example.com";
const isValidEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); // true
Метод String.match()
В отличие от test(), метод match() возвращает массив с найденными совпадениями или null, если совпадений нет.
Синтаксис:
str.match(regexp)
Примеры использования:
const text = "JavaScript делает веб-страницы JavaScript интерактивными";
// Поиск первого совпадения
const firstMatch = text.match(/JavaScript/);
console.log(firstMatch); // ["JavaScript", index: 0, input: "JavaScript делает...", groups: undefined]
// Поиск всех совпадений
const allMatches = text.match(/JavaScript/g);
console.log(allMatches); // ["JavaScript", "JavaScript"]
// Поиск с выделением групп
const sentence = "Дата публикации: 2023-06-15, автор: John Doe";
const dateMatch = sentence.match(/публикации: (\d{4}-\d{2}-\d{2})/);
console.log(dateMatch[1]); // "2023-06-15"
// Проверка наличия подстроки через match
const hasJavaScript = text.match(/JavaScript/) !== null; // true
Сравнение возможностей методов test() и match() для проверки подстрок:
| Характеристика | RegExp.test() | String.match() |
|---|---|---|
| Возвращаемое значение | Булево (true/false) | Массив совпадений или null |
| Производительность | Высокая (останавливается после первого совпадения) | Ниже (собирает все совпадения и дополнительные данные) |
| Использование для простой проверки | Предпочтительно | Избыточно |
| Получение дополнительной информации | Нет | Да (индекс, группы) |
| Глобальный поиск | Неудобно (требует дополнительного кода) | Удобно с флагом g |
Преимущества использования регулярных выражений для проверки подстрок:
- Гибкость в задании шаблонов поиска (границы слов, альтернативы, специальные символы)
- Возможность игнорировать регистр с помощью флага 'i'
- Поиск всех вхождений с помощью флага 'g'
- Выделение групп и извлечение данных из строк
Ограничения и особенности:
- Более сложный синтаксис по сравнению с includes() и indexOf()
- Потенциально более низкая производительность при неоптимальных регулярных выражениях
- Возможность создания "жадных" выражений, которые могут работать не так, как ожидается
- Необходимость экранирования специальных символов (., *, +, ?, |, (), [], {}, , ^, $)
Регулярные выражения особенно полезны, когда требуется сложная логика поиска, выходящая за рамки простой проверки наличия подстроки. Для базовых проверок рекомендуется использовать более простые методы. 🧠
Сравнение методов: производительность и совместимость
Выбор метода проверки подстроки существенно влияет на производительность и поддержку браузеров. Рассмотрим ключевые аспекты сравнения основных методов.
Производительность
Производительность методов может значительно различаться в зависимости от размера строки, сложности поиска и реализации в браузерах:
- indexOf(): Обычно самый быстрый метод для простых проверок, особенно на больших строках
- includes(): Незначительно медленнее indexOf() на некоторых движках, но разница минимальна
- RegExp.test(): Может быть быстрее для сложных шаблонов, но медленнее для простых проверок
- String.match(): Обычно самый медленный из-за сбора дополнительных данных и построения массива результатов
Сравнительные данные производительности на строке из 1 миллиона символов:
| Метод | Среднее время (мс) | Относительная скорость |
|---|---|---|
| indexOf() | 0.023 | 1x (базовое значение) |
| includes() | 0.026 | 1.13x медленнее |
| RegExp.test() (простой шаблон) | 0.156 | 6.78x медленнее |
| String.match() (простой шаблон) | 0.201 | 8.74x медленнее |
| RegExp.test() (сложный шаблон) | 0.103 | 4.48x медленнее |
Важно отметить, что для большинства практических сценариев разница в производительности незаметна, если строки не очень большие и операции не выполняются в критических циклах. 🚀
Кроссбраузерная совместимость
Совместимость с браузерами важна, особенно если ваше приложение должно поддерживать устаревшие системы:
- indexOf(): Поддерживается всеми браузерами, включая IE6+
- includes(): Поддерживается всеми современными браузерами, но не IE (требуется полифилл)
- RegExp.test() и String.match(): Хорошая поддержка во всех браузерах, но некоторые продвинутые возможности регулярных выражений могут не поддерживаться в старых версиях
Выбор оптимального метода
Рекомендации по выбору метода в зависимости от сценария использования:
- Простая проверка наличия подстроки в современных приложениях: includes() — наиболее читаемый и прямолинейный метод
- Необходимость поддержки устаревших браузеров: indexOf() !== -1
- Получение позиции подстроки: indexOf()
- Поиск без учета регистра: toLocaleLowerCase() + includes() или регулярное выражение с флагом i
- Сложные шаблоны поиска (с условиями): RegExp.test()
- Необходимость получить все совпадения и их детали: String.match() с флагом g
Примеры оптимизации для частых сценариев:
// Оптимизация для частого поиска в одной и той же строке
const text = "Это длинная строка для многократного поиска разных подстрок";
const textLower = text.toLowerCase(); // Преобразуем один раз
function checkSubstring(substring) {
return textLower.includes(substring.toLowerCase());
}
console.log(checkSubstring("длинная")); // true
console.log(checkSubstring("короткая")); // false
// Оптимизация для поиска нескольких подстрок (используем RegExp для одного прохода)
const keywords = ["JavaScript", "TypeScript", "React"];
const pattern = new RegExp(keywords.join("|"), "i");
const hasAnyKeyword = pattern.test("Разработчик должен знать JavaScript и React"); // true
// Оптимизация для проверки валидации с кешированием регулярных выражений
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
function isValidEmail(email) {
return emailRegex.test(email);
}
Для максимальной производительности:
- Избегайте создания регулярных выражений внутри циклов и функций
- Используйте indexOf() для предварительной фильтрации перед применением сложных регулярных выражений
- Кешируйте результаты преобразования строк (toLowerCase() и т.д.)
- Используйте методы String вместо регулярных выражений для простых проверок
Проверка наличия подстроки — одна из фундаментальных операций в JavaScript, и выбор правильного метода может существенно влиять на читаемость и производительность кода. Для большинства современных приложений includes() представляет оптимальный баланс между ясностью синтаксиса и эффективностью. При необходимости поддержки старых браузеров indexOf() остаётся надежным выбором, а регулярные выражения незаменимы для сложных шаблонов поиска. Помните, что преждевременная оптимизация редко оправдана — сначала делайте код понятным, а оптимизируйте только после выявления реальных проблем производительности.