A substring() é uma porcaria — às vezes.
Ela promete um corte simples de string. Pega caracteres entre índices. Devolve uma string novinha. Sem mutações, já que strings são imutáveis mesmo. Mas caramba, as pegadinhas. Devs tropeçam nisso todo dia, mesmo depois de uma década fuçando JS.
Olha essa pérola da documentação:
O método substring() extrai uma porção de uma string entre um índice inicial e um final, devolvendo a parte extraída como uma nova string. A string original nunca é modificada — strings no JavaScript são imutáveis.
Parece direto? Demais. Até você jogar inputs ruins pra ela.
Por Que a substring() Troca Seus Argumentos?
Imagina isso: “Hello, World!”.substring(5, 0). Você espera lixo — ou erro. Nada. Ela cospe “Hello”. Troca os argumentos pra você. Que babá legal, hein JS. Mas slice()? Limita pra string vazia. substr() (descansem em paz) vomita erros com negativos.
Ela é perdoona. Perdoona demais. Noobs adoram — sem crashes. Pros odeiam — previsibilidade é tudo. Olha a bagunça:
const str = "Hello, World!";
console.log(str.substring(5, 0)); // "Hello" — trocou!
console.log(str.slice(5, 0)); // ""
console.log(str.substring(-3)); // String toda — negativos viram 0
E NaN? Também vira 0. Índice além do length? Corta no .length. É o JS falando: “Relaxa, você vai me agradecer depois.” Você não vai.
Essa troca automática? Exclusiva da substring. Nasceu nos dias do Netscape, quando browsers crashavam por qualquer coisa. Herança histórica — a gente arrasta a moleza dos anos 90 pra 2024.
Um parágrafo. Bum.
Índices de string: base zero, óbvio. “JavaScript”: J=0, a=1, …, t=9. substring(start, end) pega [start, end). End exclusivo, tipo array.slice ou for-loops.
Visualiza as lacunas:
|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”. Tamanho? 4-0=4. Óbvio.
Mas mete o pé em negativos, e puff — start=0. Sem corte do final. Quer isso? Usa slice(-3). A substring finge que você quis do começo. Furtiva.
JavaScript substring() vs slice(): O Debate Eterno
Slice é o irmão mais descolado da substring. Lida com negativos. Sem trocas. Mais rígida, mais sã. Do final? slice(-5) acerta em cheio. substring(-5)? String toda.
const lang = "JavaScript";
console.log(lang.slice(0,4)); // "Java"
console.log(lang.slice(-6)); // "Script" — mágica do final
console.log(lang.substring(-6)); // "JavaScript" — falhou
Por que usar substring? Código legado. Ou esse perdão nas trocas de parsers. Mas JS moderno? Slice o dia todo. substring parece usar innerHTML em vez de textContent — funciona, mas pra quê?
Performance? Irrelevante. Ambas O(n). Benchmarks mostram slice um tiquinho à frente em loops, mas quem corta bilhões? Minha opinião quente e única: substring sobrevive porque tutoriais copiam e colam MDN de 2005. Eco chamber. Aposto: em ES2030, string views matam as duas. Sem mais cópias.
Uso real: hacks de URL. Path.substring(1) tira a barra inicial. Parsing de email:
const email = "[email protected]";
const at = email.indexOf('@');
const user = email.substring(0, at); // "dev"
const domain = email.substring(at+1); // "example.com"
Prático. Mas indexOf falha em malformados? Crash no app. Valida antes, campeão.
Truncagem: title.substring(0,50) + ‘…’. Legal — até multibyte UTF-8. “😂”.length=1? Nada, 4 bytes. substring detona emojis. Usa clusters de grapheme ou libs. Strings JS: code points, não caracteres. 2015 ligou — atualiza.
Armadilhas comuns. Off-by-one: substring(0, len) pega tudo. Mas len=string.length? Perfeito. Erra? Parcial. Trocas mascaram bugs: substring(10,5) roda como (5,10). Erro escondido.
TypeScript? string.substring(start?: number, end?: number): string. Frouxa. Adiciona overloads pra segurança.
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));
}
Exagero? Sim. Mas