Contar número de ocorrências em string em JavaScript
Neste artigo você vai aprender a contar o número de ocorrências em string na linguagem JavaScript, ou seja, quantas vezes uma string aparece
Pra contar quantas vezes uma string aparece dentro de outra em JavaScript, aplique match() com a flag global (g) e leia o length do array retornado:
const str = "Este é o meu texto que contém a palavra texto";
console.log(ocorrencias); // 2
Sem a flag g, o retorno é só a primeira ocorrência com os grupos dela, e aí a contagem vai sempre dar 1

Fala programador(a), beleza? Bora aprender mais sobre strings e também sobre JS!
Vamos utilizar uma expressão regular para atingir nosso objetivo, fazendo a relação de quantas vezes uma determinada string aparece em uma outra string
A função para aplicar a regex será a match
Nos retornando um array com todas ocorrências ou um array vazio se não houver ocorrência
Depois podemos utilizar count para receber o número
Veja o exemplo a seguir:
const str = "Este é o meu texto que contém a palavra texto"; const str2 = "Este não tem"; const ocorrencias = (str.match(/texto/g) || []).length; const ocorrencias2 = (str2.match(/texto/g) || []).length; console.log(ocorrencias); // 2 console.log(ocorrencias2); // 0
No primeiro caso temos o retorno de 2, pois são duas ocorrências encontradas no texto
E na segunda 0, pois não há a palavra texto na string alvo
Assim podemos evidencias quantas vezes uma string aparece em uma outra!
Conclusão
No artigo de hoje você aprendeu a como contar número de ocorrências em string utilizando JavaScript
Basicamente fizemos uso da função match com uma regex contendo a string que precisa ser encontrada
Formação Claude Code
Domine Claude Code do absoluto zero até o avançado
- 120 aulas
- 4 projetos
- 9h 45min
O retorno é feito por um array, que podemos obtê-lo vazio se não há ocorrências ou preenchido pelas ocorrências encontradas
Depois basta utilizar a propriedade length, que nos dá quantos elemenos o array possui, este número é a quantidade de ocorrências
Confira nossos cursos gratuitos no Youtube, com vídeos todos os dias!
Se inscreva e ative o sininho para receber as notificações e aprender mais ainda sobre desenvolvimento web!
Veja também nosso catálogo de cursos na Udemy, todos com exercícios e projetos práticos, nas mais diversas tecnologias
O link acima contém um cupom de desconto para os cursos!
Essa parte confunde muita gente, então bora destrinchar
O match() com a flag global devolve um array com todas as ocorrências completas, sem os grupos de captura
Só que quando NÃO existe nenhuma ocorrência, ele não devolve array vazio, ele devolve null
const str2 = "Este não tem";
console.log(str2.match(/texto/g)); // null
Outro detalhe importante: a flag g mudA o jogo inteiro
Com /texto/g você recebe todas as ocorrências
Com /texto/ (sem o g) você recebe só a primeira ocorrência, junto com os grupos de captura dela
Então pra contar, o g não é opcional, é obrigatório!
Contando com matchAll(), a alternativa moderna:
Se você conhece o match, o matchAll() é o primo mais completo dele
Em vez de um array pronto, ele te entrega um iterador com cada ocorrência, então dá pra espalhar num array e contar
const str = "Este é o meu texto que contém a palavra texto";
console.log(ocorrencias); // 2
A vantagem é que cada item traz a ocorrência com os grupos de captura e a posição onde ela apareceu, coisa que o match com g joga fora
Tome cuidado com um detalhe: o matchAll() exige a flag global
Se você passar uma regex sem o g, ele não conta nada, ele lança um TypeError avisando que a regex precisa ser global (a mensagem muda um pouco entre V8, Firefox e Safari)
E se a sua dúvida é sobre suporte, pode ficar tranquilo: o matchAll() está com status Baseline "amplamente disponível" e funciona em todos os navegadores principais desde janeiro de 2020 (Chrome 73, Firefox 67, Safari 13 e Edge 79), tendo atingido o Baseline Widely Available em 15 de julho de 2022
E dá pra contar sem regex? O truque do split:
Dá sim, e é bem simples
O split quebra a string em pedaços toda vez que encontra o separador
Se ele achou 2 ocorrências, ele te devolve 3 pedaços, então a conta é pedaços menos 1
const str = "Este é o meu texto que contém a palavra texto";
const ocorrencias = str.split("texto").length - 1;
console.log(ocorrencias); // 2
É útil quando o termo que você procura tem caractere especial de regex (ponto, parênteses, cifrão) e você não quer se preocupar em escapar nada
Mas se liga numa armadilha: separador string VAZIA é outra história
Quando o separador é "", a divisão acontece por unidades de código UTF-16, ou seja, não é por caractere que o usuário enxerga nem por code point, e pares substitutos (surrogate pairs) são destroçados no meio
E se a string e o separador forem os dois vazios, o retorno é um array vazio
Ou seja: pra contar caracteres de um texto com emoji ou acentuação fora do básico, o split("") vai te dar um número que não bate com o que você vê na tela
Contando um texto digitado pelo usuário (sem quebrar a regex):
Aqui mora um bug clássico
Você monta a regex com new RegExp() usando um texto que veio de um input, o usuário digita algo com ponto ou parênteses e a sua contagem vira loteria, porque esses caracteres têm significado especial dentro da expressão regular
A solução nativa é o RegExp.escape(), que escapa os caracteres especiais e te devolve uma versão segura pra usar como padrão literal
const termo = "node.js";
const str = "eu uso node.js e nodeXjs não existe";
const regex = new RegExp(RegExp.escape(termo), "g");
Sem o escape, aquele ponto casaria com qualquer caractere e o nodeXjs entraria na conta também
Um aviso honesto: o RegExp.escape() é Baseline "newly available", disponível nas versões mais recentes dos navegadores desde maio de 2025 (a proposta do TC39 chegou ao stage 4 em 18 de fevereiro de 2025)
Então em navegador ou dispositivo mais antigo ele pode simplesmente não existir
Se o seu público roda coisa antiga, o caminho do split que mostrei acima resolve o mesmo problema sem depender disso
Tome cuidado com o laço usando exec():
Muito código antigo por aí conta ocorrências rodando exec() dentro de um while com regex global
Funciona, até o dia que não funciona
Quando o padrão casa uma string de comprimento zero, o lastIndex não avança e o seu laço nunca termina, ou seja, laço infinito e aba travada
A própria documentação recomenda trocar esse tipo de código pelo String.prototype.matchAll(), que é bem menos propenso a erro
Então, se você herdou um while com exec() só pra contar, migra pro matchAll e dorme tranquilo =)
E se eu quiser trocar todas as ocorrências, não contar?
Quase todo mundo que conta acaba querendo substituir depois, então já deixo a dica aqui
O replaceAll() faz parte do ECMAScript 2021 e troca todas as ocorrências aceitando uma string literal como padrão, sem regex nenhuma
const str = "Este é o meu texto que contém a palavra texto";
console.log(str.replaceAll("texto", "conteúdo"));
Se você preferir passar uma regex como padrão, vale a mesma regra do matchAll: precisa da flag g, senão você leva um TypeError na cara
O suporte nativo começou no Chrome 85 (agosto de 2020) e no Node.js 15
Perguntas frequentes sobre contar ocorrências em string:
(bloco de FAQ com as perguntas e respostas enviadas no campo geo.faq, uma H3 por pergunta)
Não há material próprio sobre contagem de ocorrências em string (o vídeo disponível trata de conversão de string para inteiro com Number.parseInt, assunto diferente), então nenhum bloco de primeira mão foi escrito para publicação e nada com "testei/no nosso teste" deve ir ao ar. Pra ganhar um bloco legítimo depois, o que precisaria ser efetivamente executado e registrado com print ou saída de console: (1) rodar match(/x/g) numa string sem ocorrência e mostrar o null aparecendo no console; (2) rodar matchAll sem a flag g e capturar a mensagem exata de TypeError em Chrome, Firefox e Safari, comparando a diferença de texto entre os engines; (3) rodar split("") numa string com emoji e mostrar o array quebrando o par substituto; (4) testar RegExp.escape() no navegador atual e num ambiente antigo pra registrar o comportamento quando o método não existe; (5) reproduzir o laço infinito com exec() e um padrão de comprimento zero, com o antes e depois usando matchAll.
Perguntas frequentes
Como contar quantas vezes uma palavra aparece em uma string em JavaScript?
Use match() com a flag global e pegue o length:
O match com g devolve um array com todas as ocorrências completas, então o tamanho desse array é a sua contagem
O que o match() retorna quando não encontra nada?
Retorna null, não array vazio
Qual a diferença entre match e matchAll pra contar ocorrências?
O match() com a flag g devolve um array só com as ocorrências completas, sem os grupos de captura
O matchAll() devolve um iterador em que cada item traz a ocorrência com os grupos e a posição
A diferença aparece quando você precisa de mais informação além do número
Por que meu matchAll() está lançando TypeError?
Porque você passou uma regex sem a flag global
O matchAll() exige regex com g e lança TypeError caso contrário (a mensagem varia entre V8, Firefox e Safari)
A mesma regra vale pro replaceAll() quando o argumento é uma regex
Dá pra contar ocorrências sem usar expressão regular?
Dá: str.split("palavra").length – 1
O split quebra a string nos separadores, então o número de pedaços menos 1 é a quantidade de ocorrências
Só cuidado com separador vazio: split("") divide por unidades de código UTF-16, quebra pares substitutos e não corresponde ao que o usuário enxerga, e "".split("") retorna um array vazio
Como contar um termo digitado pelo usuário sem quebrar a regex?
Passe o texto pelo RegExp.escape() antes de montar o padrão com new RegExp()
Ele escapa os caracteres especiais de regex e te devolve uma versão segura pra usar como padrão literal
Lembrando que ele é Baseline newly available, disponível nas versões mais recentes dos navegadores desde maio de 2025, então em navegador ou dispositivo antigo pode não existir
O matchAll() funciona em todos os navegadores?
Funciona
Ele está com status Baseline amplamente disponível e roda em todos os navegadores principais desde janeiro de 2020 (Chrome 73, Firefox 67, Safari 13 e Edge 79), tendo atingido o Baseline Widely Available em 15 de julho de 2022
Por que meu laço com exec() trava a página?
Porque exec() em laço com regex global vira laço infinito quando o padrão casa uma string de comprimento zero: o lastIndex não avança e o while nunca termina
A recomendação da documentação é trocar esse tipo de código por String.prototype.matchAll(), que é menos propenso a erro
Leia também
Formações
Formação Vibe Coding
Do Prompt ao Produto: Crie Software Real com IA
- 474 aulas
- 20 projetos
- 39h 27min
Blog | Mais populares
As diferenças de var, let e const

Como redirecionar para outra página com JavaScript
Exercícios de lógica de programação JavaScript com gabarito
Quais são os melhores exercícios lógica de programação JavaScript? Colocar em prática os exercícios de lógica de programação em JavaScript é de extrema importância para […]

Como faria para por um input para pesquisar a palavra texto? No caso se fosse para o usuário pesquisar.