Guía completa de substring() en JavaScript

La substring() de JavaScript parece inofensiva. Pero muerde con argumentos que se intercambian solos y perdón a NaN, haciendo tropezar hasta a los veteranos.

substring() en JavaScript: caprichosa, útil y un auténtico lío — theAIcatchup

Key Takeaways

  • substring() intercambia argumentos solos y perdona NaN/negativos —práctico, pero tapa bugs.
  • Mejor slice() para negativos y predictibilidad; substring solo por legacy.
  • Valida siempre índices; las cadenas JS destrozan emojis UTF-8 sin cuidado.

Substring apesta… a veces.

Promete un corte sencillo de cadena. Extrae caracteres entre índices. Devuelve una cadena nueva. Nada de mutaciones, porque las cadenas son inmutables de todos modos. Pero ¡menudo festival de trampas! Los desarrolladores la cagan aquí a diario, incluso tras años machacando JS.

Esta joyita sacada de la documentación:

El método substring() extrae una porción de una cadena entre un índice inicial y uno final, devolviendo la parte extraída como una nueva cadena. La cadena original nunca se modifica: en JavaScript, las cadenas son inmutables.

¿Fácil? Claro. Hasta que le metes datos chungos.

¿Por qué substring() intercambia tus argumentos?

Imagina: “Hello, World!”.substring(5, 0). Esperarías basura o un error. Ni de coña. Te suelta “Hello”. Ha intercambiado los argumentos por ti. Qué niñera, JS. ¿Y slice()? Te da cadena vacía. ¿substr()? (descansen en paz) escupe errores con negativos.

Es permisiva. Demasiado. A los novatos les encanta: cero crashes. Los pros la odian: la predictibilidad manda. Aquí el desmadre:

const str = "Hello, World!";
console.log(str.substring(5, 0)); // "Hello" — ¡intercambiados!
console.log(str.slice(5, 0));     // ""
console.log(str.substring(-3));   // Toda la cadena — negativos viran a 0

¿Y NaN? También 0. ¿Índice más allá del final? Se clava en .length. Es como si JS te dijera: “Tranquilo, ya lo arreglo yo”. No lo vas a agradecer.

Ese intercambio automático es exclusivo de substring. Nació en los tiempos de Netscape, cuando los navegadores petaban por gusto. Herencia histórica: arrastramos la permisividad de los 90 hasta 2024.

Un párrafo. ¡Pum!

Índices de cadena: partiendo de cero, obvio. “JavaScript”: J=0, a=1, …, t=9. substring(start, end) agarra [start, end). End exclusivo, como array.slice o los for.

Visualiza las posiciones:

|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”. ¿Longitud? 4-0=4. Por supuesto.

Pero si te metes con negativos, ¡pum! start=0. Nada de cortar desde el final. ¿Quieres eso? Usa slice(-3). Substring finge que querías empezar desde el principio. Tramposo.

substring() vs slice() en JavaScript: el debate eterno

Slice es la hermana guay de substring. Maneja negativos. Sin intercambios. Más estricta, más cuerda. ¿Desde el final? slice(-5) lo clava. substring(-5)? Toda la cadena.

const lang = "JavaScript";
console.log(lang.slice(0,4));  // "Java"
console.log(lang.slice(-6));    // "Script" — magia desde el final
console.log(lang.substring(-6)); // "JavaScript" — fail

¿Por qué usar substring? Código legacy. O ese perdón en parsers. ¿JS moderno? Slice a tope. Substring es como innerHTML en vez de textContent: funciona, pero ¿para qué?

¿Rendimiento? Irrelevante. Ambas O(n). Benchmarks dicen que slice gana en bucles, pero ¿quién corta billones? Mi take picante: substring sobrevive porque los tutoriales copian el MDN de 2005. Círculo vicioso. Apuesto: en ES2030, las string views matan a ambas. Adiós copias.

Uso real: trucos con URLs. Path.substring(1) quita la barra inicial. Parsing de emails:

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

Práctico. Pero si indexOf falla en emails malformados, tu app revienta. Valida primero, crack.

Truncado: title.substring(0,50) + ‘…’. Bien… hasta UTF-8 multibyte. “😂”.length=1? No, 4 bytes. Substring destroza emojis. Usa clusters de grafemas o libs. Cadenas JS: code points, no caracteres. 2015 al teléfono: actualizaos.

Trampas comunes. Off-by-one: substring(0, len) coge todo. ¿len=string.length? Perfecto. ¿Error de dedo? Parcial. Los intercambios esconden bugs: substring(10,5) funciona como (5,10). Error camuflado.

¿TypeScript? string.substring(start?: number, end?: number): string. Floja. Añade overloads para seguridad.

```ts function safeSub(str: string, start: number, end?: number): string { const s = Math.m

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