Метод substring() в JavaScript: полный разбор

substring() в JavaScript выглядит безобидно. Кусается перестановкой аргументов и снисходительностью к NaN — спотыкаются даже матёрые JS-ники.

substring() в JavaScript: капризный, полезный и чертовски запутанный — theAIcatchup

Key Takeaways

  • substring() сам переставляет аргументы и прощает NaN/отрицательные — удобно, но баги прячет.
  • Берите slice() для отрицательных и предсказуемости; substring — только для legacy-толерантности.
  • Индексы всегда валидируйте; JS-строки портят UTF-8 эмодзи без присмотра.

substring() — тот ещё подлец. Иногда.

Обещает простую нарезку строки. Выдирает символы между индексами. Выдаёт свежую копию. Мутаций не жди — строки в JS неизменяемы по умолчанию. Но блин, подвохов хватает. Разрабы налетают на них ежедневно, даже спустя десять лет кодинга на JS.

Вот классика из доков:

Метод substring() вырезает кусок строки между начальным и конечным индексами, возвращая новый кусок. Оригинал не меняется — строки в JavaScript неизменяемы.

Просто? Ага. Пока не скормишь ему кривые входы.

Почему substring() переставляет аргументы?

Представьте: “Hello, World!”.substring(5, 0). Ждёте мусор или ошибку? Не-а. Получаете “Hello”. Сам переставил аргументы. Какая забота, JS. А slice()? Обрезает до пустой строки. substr() (покойся с миром) ругается на отрицательные.

Снисходителен. Слишком. Новички в восторге — крашей нет. Профи в бешенстве — предсказуемость важнее. Вот бардак:

const str = "Hello, World!";
console.log(str.substring(5, 0)); // "Hello" — переставил!
console.log(str.slice(5, 0));     // ""
console.log(str.substring(-3));   // Вся строка — отрицательные = 0

А NaN? Тоже 0. Индекс за длиной? Обрезает до .length. JS как бы говорит: «Ладно, потом спасибо скажешь». Не скажешь.

Эта перестановка? Фишка только substring. Родом из времён Netscape, когда браузеры падали от любой ерунды. Исторический багаж — тянем 1990-е поблажки в 2024-й.

Всё в одном абзаце. Бам.

Индексы строк: с нуля, ясно-понятно. “JavaScript”: J=0, a=1, …, t=9. substring(start, end) берёт [start, end). Конец не включается, как в array.slice или for-циклах.

Визуализируем:

|J|a|v|a|S|c|r|i|p|t| 0 1 2 3 4 5 6 7 8 9 10

substring(0,4): ^----^ “Java”. Длина? 4-0=4. Очевидно.

Залезьте в отрицательные — бац, start=0. Нет нарезки с конца. Хотите? slice(-3). А substring делает вид, что вы имели в виду с начала. Хитрец.

substring() против slice() в JavaScript: вечный спор

Slice — крутой брат substring. Отрицательные понимает. Не переставляет. Строже, адекватнее. С конца? slice(-5) бьёт в точку. substring(-5)? Вся строка.

const lang = "JavaScript";
console.log(lang.slice(0,4));  // "Java"
console.log(lang.slice(-6));    // "Script" — магия с конца
console.log(lang.substring(-6)); // "JavaScript" — провал

Зачем substring? Legacy-код. Или эта снисходительность в парсерах. Но в современном JS? Slice рулит. substring — как innerHTML вместо textContent: работает, но зачем?

Производительность? Разница копеечная. Оба O(n). Бенчмарки показывают лёгкое преимущество slice в циклах, но кто режет миллиарды? Мой эксклюзив: substring живёт, потому что туториалы копипастят MDN 2005 года. Эхо-камера. Ставлю: к ES2030 string views добьют обоих. Копий больше не будет.

Реальные кейсы: хаки с URL. Path.substring(1) сбрасывает ведущий слеш. Парсинг email:

const email = "[email protected]";
const at = email.indexOf('@');
const user = email.substring(0, at); // "dev"
const domain = email.substring(at+1); // "example.com"

Полезно. Но indexOf на кривом? Валит апп. Валидируй сначала, герой.

Обрезка: title.substring(0,50) + ‘…’. Норм — до UTF-8 multibyte. “😂”.length=1? Ха, 4 байта. substring калечит эмодзи. Берите grapheme clusters или либы. Строки JS — code points, не символы. 2015 звонит — апгрейдитесь.

Классические капканы. Off-by-one: substring(0, len) берёт всё. len=string.length? Ок. Опечатался? Часть. Перестановка маскирует баги: substring(10,5) работает как (5,10). Скрытый косяк.

TypeScript? string.substring(start?: number, end?: number): string. Слабо. Добавьте оверлоады для надёжности.

function safeSub(str: string, start: number, end?: number): string {
  const s = Math.max(0, start);
  const e = end === undefined ? str.length : Math.max(0, end);
  return str.substring(Math.min(s,e), Math.max(s,e));
}

Перебор? Ага. Но хаос substring просит этого.

substring() уже устарел?

Не совсем. Тянет regex-замены, DOM-текст. Но slice берёт 90% кейсов. Substr? Депрекачено в 2013 — не трогайте.

Парад ловушек:

Sarah Chen
Written by

AI research editor covering LLMs, benchmarks, and the race between frontier labs. Previously at MIT CSAIL.

Worth sharing?

Get the best AI stories of the week in your inbox — no noise, no spam.

Originally reported by dev.to