Métodos de string em JavaScript: os essenciais
Recortar com slice, trocar com replaceAll, quebrar com split e alinhar com padStart — cada método rodado no Node, com a saída real e a pegadinha de cada.
Todo dado que chega de um formulário, de uma API ou de um arquivo CSV chega como string. Antes de virar número, data ou registro no banco, ele passa por dois ou três métodos de string — e é aí que o bug nasce.
A resposta curta: para recortar use slice, para trocar use replaceAll, para
quebrar use split, para completar com zeros use padStart, e para limpar o
que veio do usuário use trim com toLowerCase. Nenhum deles altera a string
original: todos devolvem uma string nova.
Os exemplos são de uma loja: SKU, descrição de produto, aviso de frete, número
de pedido. Se você ainda não passou por
variáveis em JavaScript, vale ler antes — aqui
tudo é const.
Strings são imutáveis: nenhum método altera os caracteres do valor original. Em palavras simples, cada recorte, troca ou limpeza produz outra string, e você precisa guardar esse retorno se quiser usá-lo depois.
A bancada de recorte sempre devolve outra fita
Imagine uma loja cortando uma cópia de fita adesiva com o código do produto. A
fita original permanece no rolo; o pedaço cortado é outro material que precisa
ser recolhido. slice, replace, trim e os demais métodos funcionam assim:
leem a string atual e devolvem um novo valor, sem editar o anterior no lugar.
Antes do primeiro exemplo, anote o valor original e o retorno esperado de cada método. Depois imprima os dois lado a lado. Repita uma chamada sem atribuir o retorno e confira qual variável mudou. Essa microprática torna a imutabilidade visível no console.
slice(inicio, fim) devolve o pedaço entre os dois índices, com o fim
exclusivo. O índice começa em zero.
const produto = 'Teclado mecânico ABNT2';
console.log(produto.length);
console.log(produto.slice(0, 7));
console.log(produto.slice(-5));
console.log(produto.substring(0, 7));
console.log(produto.substring(7, 0));
console.log(produto.slice(7, 0));As quatro primeiras linhas fazem o esperado. As duas últimas são a diferença
entre os dois métodos: com os argumentos invertidos, substring troca os dois
de lugar sem avisar e devolve 'Teclado' de novo, enquanto slice devolve
string vazia — a última linha da saída está em branco.
Fica mais claro imprimindo com aspas:
const produto = 'Teclado mecânico ABNT2';
console.log(JSON.stringify(produto.substring(7, 0)));
console.log(JSON.stringify(produto.slice(7, 0)));A segunda diferença é o índice negativo. slice conta a partir do fim;
substring trata qualquer negativo como zero:
const sku = 'TEC-MEC-ABNT2-001';
console.log(sku.slice(-3));
console.log(sku.substring(-3));
console.log(sku.at(-1));
console.log(sku.charAt(sku.length - 1));Pegar os três últimos caracteres de um SKU é uma tarefa de todo dia. Com
substring você recebe a string inteira e não percebe até o pedido errado sair.
Existe ainda o substr, que recebe posição e quantidade em vez de dois
índices. Ele funciona, mas é um anexo legado da especificação, mantido só por
compatibilidade com a web antiga:
const sku = 'TEC-MEC-ABNT2-001';
console.log(sku.substr(4, 3));
console.log(sku.slice(4, 4 + 3));Achar antes de recortar
Recortar por índice fixo só serve quando o formato é fixo. No resto dos casos você primeiro procura:
const descricao = 'Teclado mecânico ABNT2 com switch azul e cabo USB-C';
console.log(descricao.indexOf('switch'));
console.log(descricao.indexOf('Switch'));
console.log(descricao.includes('switch'));
console.log(descricao.toLowerCase().includes('switch'));
console.log(descricao.startsWith('Teclado'));
console.log(descricao.endsWith('USB-C'));indexOf devolve -1 quando não acha — e -1 é um número verdadeiro em
condição booleana. if (descricao.indexOf('Switch')) entra no if justamente
quando não achou. Use includes quando a pergunta é “tem ou não tem”, e
guarde indexOf para quando você precisa da posição.
Repare também que a busca diferencia maiúscula de minúscula. Comparação de texto
digitado por gente sempre passa por toLowerCase dos dois lados.
replace troca uma; replaceAll troca todas
Esta é a pegadinha que mais aparece em code review de iniciante:
const aviso = 'frete grátis em SP, frete grátis em RJ, frete grátis em MG';
console.log(aviso.replace('frete grátis', 'frete fixo'));
console.log(aviso.replaceAll('frete grátis', 'frete fixo'));
console.log(aviso.replace(/frete grátis/g, 'frete fixo'));Com um texto simples como primeiro argumento, replace troca a primeira
ocorrência e para. Não é bug: é a especificação. As duas formas de trocar tudo
são replaceAll ou uma expressão regular com a flag g.
Misturar as duas é erro em tempo de execução, e de propósito:
const aviso = 'frete grátis em SP, frete grátis em RJ';
console.log(aviso.replaceAll(/frete grátis/, 'frete fixo'));TypeError: String.prototype.replaceAll called with a non-global RegExp argument at String.replaceAll (<anonymous>) at file:///private/tmp/loja/aviso-frete.mjs:3:19 Node.js v24.16.0
A linguagem prefere parar a te deixar achar que trocou tudo. É uma das poucas
vezes em que um TypeError é um favor.
Tem mais uma armadilha, e essa é silenciosa: a string de substituição tem
sintaxe própria. $& significa “o trecho encontrado”, e $$ significa um
cifrão literal:
const nome = 'mouse sem fio';
console.log(nome.replace('mouse', '$& gamer'));
console.log(nome.replace('mouse', '$$ gamer'));
console.log(nome.replace('mouse', () => '$& gamer'));Se o texto que você está inserindo vem de fora — um preço R$ 99,00, por
exemplo — passe uma função como segundo argumento. Dentro dela o retorno é
literal, sem interpretação de $.
split e join: da linha do arquivo ao array
split quebra a string num array usando um separador; join faz o caminho de
volta.
const linha = 'Teclado mecânico;289,90;2';
const [nome, preco, quantidade] = linha.split(';');
console.log(nome, preco, quantidade);
console.log(linha.split(';', 2));
console.log(['Teclado', 'Mouse', 'Headset'].join(' + '));O segundo argumento de split é um limite de itens, não a posição onde
parar de ler. Ele descarta o resto em vez de juntar — quase nunca é o que você
quer num CSV.
Onde split('') mente: acento e emoji
Separador vazio parece a forma óbvia de “pegar caractere por caractere”. Ela funciona para texto ASCII e quebra em tudo que o Brasil escreve:
const cliente = 'Ana Café 🛒';
console.log(cliente.length);
console.log(cliente.split(''));
console.log([...cliente]);
console.log([...cliente].length);O carrinho virou dois pedaços inválidos. length e split('') trabalham em
unidades de código UTF-16, e um emoji ocupa duas. O spread ([...texto]) e o
Array.from percorrem por ponto de código e devolvem o emoji inteiro.
O acento tem um problema parecido, e mais sorrateiro, porque os dois textos parecem idênticos na tela:
const composto = 'Café';
const decomposto = 'Café';
console.log(composto, decomposto);
console.log(composto.length, decomposto.length);
console.log(composto === decomposto);
console.log(decomposto.split(''));
console.log(composto === decomposto.normalize('NFC'));Duas strings visualmente iguais, comprimentos diferentes, comparação false.
Isso acontece de verdade: macOS costuma entregar nomes de arquivo decompostos, e
formulário colado do Word vem misturado. Se você compara texto com acento,
normalize('NFC') dos dois lados antes.
Para busca em catálogo, o caminho é decompor e jogar fora os acentos:
function normalizar(texto) {
return texto
.trim()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase();
}
const catalogo = ['Teclado mecânico ABNT2', 'Mouse sem fio', 'Óculos VR', 'Cabo HDMI'];
console.log(normalizar('Teclado mecânico ABNT2'));
console.log(catalogo.filter((p) => normalizar(p).includes(normalizar(' MECANICO '))));
console.log(catalogo.filter((p) => normalizar(p).includes(normalizar('oculos'))));NFD separa a letra do acento; o replace apaga a faixa de sinais
diacríticos. O cliente digita “oculos” e acha “Óculos VR”.
padStart: número de pedido, cartão e recibo alinhado
padStart(tamanho, preenchimento) completa à esquerda até a string ter o
tamanho pedido. padEnd faz o mesmo à direita.
const pedido = 47;
console.log(String(pedido).padStart(6, '0'));
console.log(`#${String(pedido).padStart(6, '0')}`);
console.log('5432'.padStart(16, '*'));
console.log('Subtotal'.padEnd(14, '.') + 'R$ 289,90');
console.log('Frete'.padEnd(14, '.') + 'R$ 12,50');Repare no String(pedido): padStart é método de string, e pedido é número.
Sem a conversão, o programa quebra — é exatamente o erro da última seção.
trim e a limpeza do que veio do formulário
Espaço no começo, espaço no fim, quebra de linha invisível colada junto: é o estado normal de um campo de texto preenchido por gente.
const digitado = ' Ana@CLIENTE.com.br \n';
console.log(JSON.stringify(digitado));
console.log(JSON.stringify(digitado.trim()));
console.log(digitado.trim().toLowerCase());
console.log(JSON.stringify(' PRIMEIRACOMPRA '.trimStart()));
console.log(JSON.stringify(' PRIMEIRACOMPRA '.trimEnd()));JSON.stringify na hora de depurar string é um truque barato e muito útil: ele
mostra as aspas e escapa a quebra de linha, então você vê o espaço que
estava lá.
Toda string é imutável
Nenhum método de string altera a original. Todos devolvem uma nova:
const cupom = 'primeiracompra';
cupom.toUpperCase();
console.log(cupom);
console.log(cupom.toUpperCase());A segunda linha do código não faz nada: calcula uma string maiúscula e joga fora. Isso não gera erro nem aviso — só um campo que continua minúsculo em produção. Se o resultado importa, ele precisa ser atribuído ou usado na hora.
Erros comuns
O campeão é chamar método de string num valor que não é string. Acontece toda
vez que um campo numérico do banco ou do JSON encontra um toUpperCase,
padStart ou trim:
const pedido = { id: 4821, cupom: 2026 };
console.log(pedido.cupom.toUpperCase());TypeError: pedido.cupom.toUpperCase is not a function at file:///private/tmp/loja/cupom-numero.mjs:3:26 Node.js v24.16.0
Leia o caminho que o erro imprime: pedido.cupom.toUpperCase. Ele está dizendo
que pedido.cupom existe — se não existisse, a mensagem seria
Cannot read properties of undefined
— e que o valor guardado ali não tem esse método. Um número não tem
toUpperCase.
A correção é converter explicitamente:
const pedido = { id: 4821, cupom: 2026 };
console.log(String(pedido.cupom).toUpperCase());
console.log(String(pedido.cupom).padStart(8, '0'));String(valor) funciona até com null e undefined, devolvendo 'null' e
'undefined'. Se isso for pior do que quebrar, valide antes em vez de
converter cego.
Os outros dois erros que se repetem já apareceram acima, e vale reunir:
replacecom texto simples trocando só a primeira ocorrência, e ninguém percebendo porque o teste tinha uma ocorrência só.- método chamado sem atribuir o resultado, por hábito de linguagem em que string é mutável.
Qual método usar em cada situação
| preciso | método | cuidado |
|---|---|---|
| pegar um pedaço | slice(inicio, fim) |
fim é exclusivo; negativo conta do fim |
| pegar o último caractere | at(-1) |
charAt não aceita negativo |
| saber se contém | includes |
indexOf devolve -1, que é verdadeiro |
| trocar todas as ocorrências | replaceAll |
replace com texto troca só a primeira |
| quebrar em array | split(separador) |
split('') estraga emoji e acento decomposto |
| percorrer caractere a caractere | [...texto] |
length conta unidades UTF-16 |
| completar com zeros | padStart(n, '0') |
converta o número com String() antes |
| limpar entrada de formulário | trim().toLowerCase() |
e normalize('NFC') se houver acento |
O próximo passo natural é o outro lado da mesma moeda: transformar essas strings
em números em JavaScript — e descobrir
por que '1.234,56' vira NaN. Se quiser ver onde esta lição cai no roteiro
inteiro, o guia completo de JavaScript mostra a ordem
de estudo, e a trilha de JavaScript lista as lições
publicadas em sequência.
Perguntas frequentes
Qual a diferença prática entre slice e substring?
Por que meu replace só trocou a primeira ocorrência?
Por que texto.length devolve um número maior do que os caracteres que vejo?
Preciso guardar o retorno de toUpperCase numa variável?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- MDN — String — developer.mozilla.org
- MDN — String.prototype.replaceAll() — developer.mozilla.org
- ECMAScript 2026 Language Specification — String Objects — tc39.es


