Методы строк — полное руководство в TypeScript: подробное руководство
Что это такое и зачем? (аналогия из реальной жизни)
Представьте, что вы работаете в библиотеке. У вас есть книга — это строка (текст). Но просто иметь книгу мало. Вам нужно уметь с ней работать: найти нужную главу, заменить устаревшие слова, вырезать цитату, проверить, начинается ли она с заглавной буквы, или посчитать, сколько в ней страниц.
В программировании методы строк — это те самые инструменты, которые позволяют вам манипулировать текстом. Строка — это не просто набор букв, это объект, у которого есть встроенные функции (методы). Они умеют:
- Искать подстроку (как найти главу в книге)
- Заменять части текста (как заменить устаревшее слово на новое)
- Вырезать часть строки (как сделать вырезку из газеты)
- Проверять, соответствует ли строка шаблону (как проверить, является ли строка email-адресом)
- Изменять регистр (сделать все буквы заглавными или строчными)
- Удалять лишние пробелы (как подстричь газон)
Зачем это нужно? Представьте, что вы пишете программу для регистрации пользователей. Вам нужно:
- Убрать лишние пробелы из имени (
" Иван "→"Иван") - Проверить, что email содержит символ
@ - Сделать первую букву фамилии заглавной
- Заменить в тексте все плохие слова на звёздочки
Без методов строк вам пришлось бы писать сложные циклы и проверки вручную. С методами — это одна-две строчки кода.
Важно: Строки в TypeScript (и JavaScript) — это неизменяемые (immutable) типы данных. Это значит, что любой метод строки не меняет исходную строку, а создаёт новую. Представьте, что вы делаете ксерокопию страницы и работаете с копией — оригинал остаётся нетронутым.
Теория: как это работает изнутри
Чтобы понять, как работают методы строк, нужно заглянуть "под капот" TypeScript.
Строка как массив символов
Внутри компьютера строка хранится как упорядоченная последовательность символов (символ — это буква, цифра, знак препинания или пробел). У каждого символа есть свой индекс — номер позиции, начиная с нуля.
Например, строка "Привет" выглядит так:
Индексы: 0 1 2 3 4 5
Символы: П р и в е т
Большинство методов строк используют эту нумерацию. Когда вы говорите "найди символ на позиции 3", компьютер мгновенно обращается к ячейке памяти с этим индексом.
Unicode и кодировка
Современные строки в TypeScript хранятся в кодировке UTF-16. Это значит, что каждый символ может занимать 2 или 4 байта. Для обычных букв (латиница, кириллица) — 2 байта. Для редких символов (например, эмодзи 🎉) — 4 байта.
Это важно знать, потому что некоторые методы (например, charAt) работают с 2-байтовыми единицами, и эмодзи могут "сломаться". Для работы с эмодзи лучше использовать современные методы (ES6+), которые понимают полные символы.
Примитив vs Объект
В TypeScript строки могут быть двух видов:
- Примитив (
string):let name = "Анна";— простое значение. - Объект (
String):let name = new String("Анна");— полноценный объект с методами.
На практике вы почти всегда будете использовать примитивы. Когда вы вызываете метод на примитиве, TypeScript временно "оборачивает" его в объект, выполняет метод и возвращает результат. Это называется автоупаковка (autoboxing).
Совет: Никогда не используйтеnew String(). Это приводит к путанице, потому чтоtypeof new String("текст")вернёт"object", а не"string". Всегда используйте строковые литералы:"текст"или'текст'.
Методы — это функции, привязанные к строке
Каждый метод — это функция, которая "живёт" внутри прототипа строки (String.prototype). Когда вы пишете "hello".toUpperCase(), TypeScript:
- Берёт строку
"hello" - Ищет метод
toUpperCaseв прототипе - Выполняет его, создавая новую строку
"HELLO" - Возвращает новую строку (оригинал не меняется)
Синтаксис в TypeScript: все формы и варианты
В TypeScript методы строк вызываются через точку после строки (или переменной, содержащей строку). Общий синтаксис:
строка.имяМетода(аргументы)
Где:
строка— это либо строковый литерал ("текст"), либо переменная (let s = "текст")имяМетода— одно из стандартных имён (например,toUpperCase,slice)аргументы— параметры, которые принимает метод (могут быть обязательными или опциональными)
Полный справочник методов (таблица)
| Метод | Описание | Возвращает | Пример |
|---|---|---|---|
charAt(index) |
Возвращает символ по указанному индексу | Строка (1 символ) | "Привет".charAt(0) → "П" |
charCodeAt(index) |
Возвращает Unicode-код символа | Число | "A".charCodeAt(0) → 65 |
concat(str1, str2, ...) |
Объединяет строки (лучше использовать + или шаблонные строки) | Новая строка | "Hello".concat(" ", "World") → "Hello World" |
includes(searchString, position?) |
Проверяет, содержит ли строка подстроку | Boolean | "Hello".includes("ell") → true |
endsWith(searchString, length?) |
Проверяет, заканчивается ли строка на подстроку | Boolean | "Hello".endsWith("lo") → true |
indexOf(searchString, position?) |
Возвращает индекс первого вхождения подстроки (или -1) | Число | "Hello".indexOf("l") → 2 |
lastIndexOf(searchString, position?) |
Возвращает индекс последнего вхождения подстроки | Число | "Hello".lastIndexOf("l") → 3 |
localeCompare(compareString) |
Сравнивает строки с учётом локали (для сортировки) | Число (-1, 0, 1) | "а".localeCompare("б") → -1 |
match(regexp) |
Ищет совпадения с регулярным выражением | Массив или null | "a1b2".match(/d+/) → ["1"] |
matchAll(regexp) |
Ищет все совпадения с регулярным выражением (ES2020) | Итератор | [... "a1b2".matchAll(/d/g)] → ["1", "2"] |
normalize(form?) |
Приводит строку к нормализованной форме Unicode | Новая строка | "é".normalize() → "é" |
padEnd(targetLength, padString?) |
Дополняет строку справа до указанной длины | Новая строка | "5".padEnd(3, "0") → "500" |
padStart(targetLength, padString?) |
Дополняет строку слева до указанной длины | Новая строка | "5".padStart(3, "0") → "005" |
repeat(count) |
Повторяет строку указанное количество раз | Новая строка | "Ha".repeat(3) → "HaHaHa" |
replace(searchValue, replaceValue) |
Заменяет первое вхождение подстроки | Новая строка | "cat".replace("c", "b") → "bat" |
replaceAll(searchValue, replaceValue) |
Заменяет все вхождения подстроки (ES2021) | Новая строка | "cat cat".replaceAll("cat", "dog") → "dog dog" |
search(regexp) |
Ищет совпадение с регулярным выражением, возвращает индекс | Число | "abc123".search(/d/) → 3 |
slice(beginIndex, endIndex?) |
Извлекает часть строки (от begin до end, не включая end) | Новая строка | "Hello".slice(1, 4) → "ell" |
split(separator, limit?) |
Разбивает строку на массив по разделителю | Массив строк | "a,b,c".split(",") → ["a", "b", "c"] |
startsWith(searchString, position?) |
Проверяет, начинается ли строка с подстроки | Boolean | "Hello".startsWith("He") → true |
substring(indexStart, indexEnd?) |
Извлекает часть строки (похож на slice, но с особенностями) | Новая строка | "Hello".substring(1, 4) → "ell" |
toLocaleLowerCase(locale?) |
Переводит в нижний регистр с учётом локали | Новая строка | "İ".toLocaleLowerCase("tr") → "i" |
toLocaleUpperCase(locale?) |
Переводит в верхний регистр с учётом локали | Новая строка | "i".toLocaleUpperCase("tr") → "İ" |
toLowerCase() |
Переводит все символы в нижний регистр | Новая строка | "Hello".toLowerCase() → "hello" |
toString() |
Возвращает строковое представление объекта | Строка | (123).toString() → "123" |
toUpperCase() |
Переводит все символы в верхний регистр | Новая строка | "Hello".toUpperCase() → "HELLO" |
trim() |
Удаляет пробелы с обоих концов строки | Новая строка | " Hi ".trim() → "Hi" |
trimEnd() / trimRight() |
Удаляет пробелы в конце строки | Новая строка | "Hi ".trimEnd() → "Hi" |
trimStart() / trimLeft() |
Удаляет пробелы в начале строки | Новая строка | " Hi".trimStart() → "Hi" |
valueOf() |
Возвращает примитивное значение строки | Строка | new String("x").valueOf() → "x" |
[Symbol.iterator] |
Позволяет перебирать строку по символам (через for...of) | Итератор | for (let ch of "Hi") { ... } |
Примечание: Методыsubstringиsliceочень похожи, но есть важное отличие:substringменяет местами аргументы, если первый больше второго, аsliceвозвращает пустую строку. Такжеsliceподдерживает отрицательные индексы (отсчёт с конца), аsubstring— нет. Рекомендуется всегда использоватьslice.
Примеры кода — от простого к сложному
Пример 1: Базовые методы (регистр, обрезка, поиск)
// Объявляем исходную строку
let message: string = " Привет, Мир! JavaScript — это весело. ";
// 1. toUpperCase() — переводим всё в верхний регистр
let upperMessage: string = message.toUpperCase();
console.log(upperMessage); // " ПРИВЕТ, МИР! JAVASCRIPT — ЭТО ВЕСЕЛО. "
// 2. toLowerCase() — переводим всё в нижний регистр
let lowerMessage: string = message.toLowerCase();
console.log(lowerMessage); // " привет, мир! javascript — это весело. "
// 3. trim() — удаляем пробелы в начале и конце
let trimmedMessage: string = message.trim();
console.log(trimmedMessage); // "Привет, Мир! JavaScript — это весело."
// 4. includes() — проверяем, содержит ли строка подстроку
let hasJavaScript: boolean = message.includes("JavaScript");
console.log(`Содержит "JavaScript"? ${hasJavaScript}`); // true
// 5. indexOf() — находим индекс первого вхождения
let indexOfComma: number = message.indexOf(",");
console.log(`Индекс запятой: ${indexOfComma}`); // 9 (с учётом пробелов в начале)
// 6. charAt() — получаем символ по индексу
let firstChar: string = message.charAt(2); // 2, потому что первые два символа — пробелы
console.log(`Третий символ (с учётом пробелов): "${firstChar}"`); // "П"
Объяснение: В этом примере мы видим, как работают самые популярные методы. Обратите внимание, что trim() не меняет исходную строку — он возвращает новую. Метод includes() очень удобен для проверки наличия подстроки без необходимости вычислять индекс и сравнивать с -1.
Пример 2: Извлечение частей строки (slice, substring, split)
let url: string = "https://example.com/users/12345";
// 1. slice() — извлекаем часть строки
// Берём символы с 8-го по 19-й (не включая 19)
let domain: string = url.slice(8, 19);
console.log(`Домен: ${domain}`); // "example.com"
// Отрицательные индексы — отсчёт с конца
let lastPart: string = url.slice(-5);
console.log(`Последние 5 символов: ${lastPart}`); // "12345"
// 2. substring() — похож на slice, но не поддерживает отрицательные индексы
let domainSubstring: string = url.substring(8, 19);
console.log(`Домен через substring: ${domainSubstring}`); // "example.com"
// 3. split() — разбиваем строку на массив
let parts: string[] = url.split("/");
console.log("Части URL:", parts);
// ["https:", "", "example.com", "users", "12345"]
// Извлекаем ID пользователя (последний элемент)
let userId: string = parts[parts.length - 1];
console.log(`ID пользователя: ${userId}`); // "12345"
// 4. Разбиваем строку с ограничением
let limitedParts: string[] = url.split("/", 3);
console.log("Первые 3 части:", limitedParts);
// ["https:", "", "example.com"]
Объяснение: slice() — самый гибкий метод для извлечения подстрок. Он поддерживает отрицательные индексы, что позволяет легко брать символы с конца. split() превращает строку в массив — это незаменимо при парсинге данных (например, CSV-файлов или URL).
Пример 3: Замена текста (replace, replaceAll)
let text: string = "Кошка гуляет по крыше. Кошка ловит мышей.";
// 1. replace() — заменяет только ПЕРВОЕ вхождение
let firstReplacement: string = text.replace("Кошка", "Собака");
console.log("После replace:", firstReplacement);
// "Собака гуляет по крыше. Кошка ловит мышей."
// 2. replaceAll() — заменяет ВСЕ вхождения (ES2021)
let allReplacement: string = text.replaceAll("Кошка", "Собака");
console.log("После replaceAll:", allReplacement);
// "Собака гуляет по крыше. Собака ловит мышей."
// 3. replace с регулярным выражением (флаг g — глобальный поиск)
let regexReplacement: string = text.replace(/Кошка/g, "Тигр");
console.log("С регулярным выражением:", regexReplacement);
// "Тигр гуляет по крыше. Тигр ловит мышей."
// 4. replace с функцией обратного вызова
let fancyReplacement: string = text.replace(/Кошка/g, (match: string) => {
return match.toUpperCase() + " 🐱";
});
console.log("С функцией:", fancyReplacement);
// "КОШКА 🐱 гуляет по крыше. КОШКА 🐱 ловит мышей."
Объяснение: replace() без флага g заменяет только первое совпадение. replaceAll() — это современный метод (2021 год), который заменяет все вхождения без использования регулярных выражений. Если вам нужно что-то сложнее (например, замена с учётом регистра или динамическая замена), используйте регулярные выражения.
Пример 4: Работа с пробелами и форматирование
// 1. padStart() и padEnd() — дополнение строки до нужной длины
let number: string = "42";
let paddedStart: string = number.padStart(5, "0");
console.log(`padStart: ${paddedStart}`); // "00042"
let paddedEnd: string = number.padEnd(5, "!");
console.log(`padEnd: ${paddedEnd}`); // "42!!!"
// 2. repeat() — повторение строки
let laugh: string = "Ха";
let manyLaughs: string = laugh.repeat(5);
console.log(`repeat: ${manyLaughs}`); // "ХаХаХаХаХа"
// 3. trim(), trimStart(), trimEnd()
let messyString: string = " пробелы ";
console.log(`trim: "${messyString.trim()}"`); // "пробелы"
console.log(`trimStart: "${messyString.trimStart()}"`); // "пробелы "
console.log(`trimEnd: "${messyString.trimEnd()}"`); // " пробелы"
// 4. Практический пример: форматирование номера телефона
let phone: string = "1234567890";
let formattedPhone: string =
phone.slice(0, 3).padStart(4, "+7") + " " +
phone.slice(3, 6) + " " +
phone.slice(6, 8) + " " +
phone.slice(8, 10);
console.log(`Форматированный телефон: ${formattedPhone}`);
// "+712 345 67 89"
Объяснение: padStart и padEnd часто используются для форматирования чисел (например, добавление ведущих нулей). repeat() удобен для создания повторяющихся паттернов. trim() — обязательный метод для очистки пользовательского ввода.
Пример 5: Проверка начала и конца строки
let filename: string = "document.pdf";
let email: string = "user@example.com";
let code: string = " let x = 5; ";
// 1. startsWith() — проверка начала строки
let isPdf: boolean = filename.startsWith("document");
console.log(`Файл начинается с "document"? ${isPdf}`); // true
let isHttp: boolean = email.startsWith("http");
console.log(`Email начинается с "http"? ${isHttp}`); // false
// 2. endsWith() — проверка конца строки
let isPdfFile: boolean = filename.endsWith(".pdf");
console.log(`Файл имеет расширение .pdf? ${isPdfFile}`); // true
let isComDomain: boolean = email.endsWith(".com");
console.log(`Email заканчивается на .com? ${isComDomain}`); // true
// 3. Практический пример: валидация расширения файла
function validateFile(fileName: string): boolean {
const allowedExtensions: string[] = [".jpg", ".png", ".gif", ".pdf"];
const lowerCaseName: string = fileName.toLowerCase();
return allowedExtensions.some(ext => lowerCaseName.endsWith(ext));
}
console.log(`Файл "photo.JPG" допустим? ${validateFile("photo.JPG")}`); // true
console.log(`Файл "script.exe" допустим? ${validateFile("script.exe")}`); // false
// 4. startsWith с позицией
let sentence: string = "Hello, world!";
console.log(sentence.startsWith("world", 7)); // true (начинается с 7-го индекса)
Объяснение: startsWith() и endsWith() — это более читаемая альтернатива indexOf() для проверки начала и конца строки. Они принимают необязательный второй аргумент — позицию, с которой начинать поиск.
Пример 6: Сравнение строк и работа с Unicode
// 1. localeCompare() — сравнение с учётом локали
let word1: string = "яблоко";
let word2: string = "ананас";
let comparisonResult: number = word1.localeCompare(word2, "ru");
console.log(`Сравнение "яблоко" и "ананас": ${comparisonResult}`);
// 1 (потому что "я" > "а" в русском алфавите)
// 2. Сортировка массива строк с учётом локали
let fruits: string[] = ["яблоко", "ананас", "банан", "вишня"];
fruits.sort((a, b) => a.localeCompare(b, "ru"));
console.log("Отсортированные фрукты:", fruits);
// ["ананас", "банан", "вишня", "яблоко"]
// 3. charCodeAt() — получение Unicode-кода
let char: string = "A";
let unicodeCode: number = char.charCodeAt(0);
console.log(`Unicode код "A": ${unicodeCode}`); // 65
// 4. fromCharCode() — создание строки из кода (статический метод)
let charFromCode: string = String.fromCharCode(65, 66, 67);
console.log(`Из кодов 65, 66, 67: "${charFromCode}"`); // "ABC"
// 5. Работа с эмодзи (суррогатные пары)
let emoji: string = "🎉";
console.log(`Длина эмодзи: ${emoji.length}`); // 2 (потому что эмодзи занимает 2 символа UTF-16)
console.log(`Первый символ: ${emoji.charAt(0)}`); // "uD83C" (нечитаемый)
console.log(`Второй символ: ${emoji.charAt(1)}`); // "uDF89" (нечитаемый)
// Правильный способ перебора символов (ES6)
for (let symbol of emoji) {
console.log(`Символ: ${symbol}`); // "🎉" (один символ)
}
Объяснение: localeCompare() — единственный правильный способ сортировки строк на русском (и других языках). Обычное сравнение через < и > работает неправильно для букв "ё", "й" и т.д. При работе с эмодзи всегда используйте for...of или оператор расширения ([...str]), а не charAt().
Пример 7: Комплексный пример — парсинг и валидация данных
// Представьте, что мы получаем данные от пользователя в виде строки
let userInput: string = " Иван Петров, ivan@example.com, +7 (999) 123-45-67 ";
// Функция для очистки и разбора данных
function parseUserData(input: string): object | null {
// 1. Убираем лишние пробелы
const trimmed: string = input.trim();
// 2. Проверяем, что строка не пустая
if (trimmed.length === 0) {
console.log("Ошибка: пустая строка");
return null;
}
// 3. Разбиваем строку по запятой
const parts: string[] = trimmed.split(",");
// 4. Проверяем, что у нас ровно 3 части
if (parts.length !== 3) {
console.log("Ошибка: неверный формат. Ожидается: имя, email, телефон");
return null;
}
// 5. Очищаем каждую часть от пробелов
const name: string = parts[0].trim();
const email: string = parts[1].trim();
const phone: string = parts[2].trim();
// 6. Валидация имени (не должно быть пустым)
if (name.length === 0) {
console.log("Ошибка: имя не может быть пустым");
return null;
}
// 7. Валидация email (должен содержать @ и .)
if (!email.includes("@") || !email.includes(".")) {
console.log("Ошибка: неверный формат email");
return null;
}
// 8. Валидация телефона (должен содержать только цифры, +, пробелы, скобки, тире)
const phoneClean: string = phone.replace(/[^d+]/g, ""); // Удаляем всё, кроме цифр и +
if (phoneClean.length < 10) {
console.log("Ошибка: неверный формат телефона");
return null;
}
//