image

Bootcamps ilimitados y a más de 750 cursos para siempre

70
%OFF
Sophie Ale
Sophie Ale18/10/2026 11:21
Compartir

Unicode no JavaScript: por que .length pode enganar?

  • #Web3
  • #Python
  • #JavaScript

Já tentou limitar a quantidade de caracteres de um campo e percebeu que um emoji estava ocupando mais espaço do que deveria?

Imagine que você está criando um formulário para um aplicativo. O usuário pode escolher um nome com até 15 caracteres. Tudo funciona bem até alguém adicionar emojis, letras estilizadas ou caracteres com acentos.

O problema pode estar em uma das propriedades mais conhecidas do JavaScript: .length.

Ela não está errada. Na verdade, está fazendo exatamente o que foi projetada para fazer. A questão é que nem sempre estamos medindo aquilo que imaginamos.

Neste artigo, vamos entender como o JavaScript interpreta caracteres Unicode, por que alguns emojis são contados mais de uma vez e como resolver isso usando Intl.Segmenter.

1. O problema começa com um simples emoji

Vamos começar com um exemplo:

const texto = "Olá";

console.log(texto.length); // 3

Até aqui, nenhuma surpresa. Temos três letras e o JavaScript retorna 3.

Agora, experimente:

const emoji = "😄";

console.log(emoji.length); // 2

Como assim 2?

Visualmente, temos apenas um emoji. Mas o JavaScript retorna 2 porque a propriedade .length conta unidades de código UTF-16.

Isso significa que um caractere representado por dois elementos UTF-16 será contado duas vezes, mesmo quando aparece como um único símbolo na tela.

E a situação fica ainda mais interessante quando utilizamos emojis compostos.

const emoji = "👩‍💻";

console.log(emoji.length); // 5

Um único emoji de uma mulher usando computador ocupa cinco unidades de código UTF-16.

Se você utiliza .length para validar o tamanho de nomes, apelidos ou mensagens, essa diferença pode causar resultados inesperados.

2. Entendendo a diferença entre UTF-16, Unicode e caracteres visíveis

Para compreender o comportamento do JavaScript, precisamos separar três conceitos.

Unidades de código UTF-16

O JavaScript representa strings usando unidades de código UTF-16. Algumas letras precisam de apenas uma unidade, enquanto outros caracteres Unicode precisam de duas.

É isso que .length contabiliza.

Pontos de código Unicode

Um ponto de código identifica um elemento do repertório Unicode.

Por exemplo, o emoji 😄 corresponde ao ponto de código U+1F604.

Podemos contar pontos de código utilizando o operador spread:

const emoji = "😄";

console.log(emoji.length);      // 2
console.log([...emoji].length); // 1

Isso resolve parte do problema, mas não todos os casos.

Clusters de grafemas

Um cluster de grafemas representa aproximadamente aquilo que uma pessoa percebe como um único caractere.

Alguns emojis são formados pela combinação de vários pontos de código. Mesmo assim, aparecem como um único símbolo.

Veja:

const emoji = "👩‍💻";

console.log(emoji.length);      // 5
console.log([...emoji].length); // 3

O resultado mostra cinco unidades UTF-16 e três pontos de código.

Mas continuamos vendo apenas um emoji.

Para contar esse símbolo como uma única unidade visual, precisamos de outra abordagem.

3. A solução com Intl.Segmenter

O JavaScript disponibiliza a API Intl.Segmenter, que permite dividir textos em unidades linguísticas, como palavras, frases ou clusters de grafemas.

Veja como utilizá-la:

const texto = "👩‍💻";

const segmentador = new Intl.Segmenter("pt-BR", {
granularity: "grapheme"
});

const caracteres = [...segmentador.segment(texto)];

console.log(caracteres.length); // 1

Agora temos o resultado esperado: 1.

A opção granularity: "grapheme" informa que queremos separar o texto por clusters de grafemas.

Isso é útil quando a quantidade de caracteres deve corresponder, aproximadamente, à percepção visual do usuário.

Podemos transformar esse exemplo em uma função reutilizável.

const segmentador = new Intl.Segmenter("pt-BR", {
granularity: "grapheme"
});

function contarCaracteres(texto) {
return [...segmentador.segment(texto)].length;
}

console.log(contarCaracteres("Olá"));    // 3
console.log(contarCaracteres("😄"));     // 1
console.log(contarCaracteres("👩‍💻"));   // 1

Uma observação importante: Intl.Segmenter é compatível com navegadores modernos, mas pode não estar disponível em ambientes antigos. Antes de utilizá-lo em produção, confira a compatibilidade dos navegadores e da versão do Node.js do seu projeto.

4. Aplicação prática: limitando caracteres em um nickname

Agora vamos imaginar uma situação comum no desenvolvimento de aplicações.

Você está construindo um formulário que permite criar nicknames para um jogo. O limite é de 15 caracteres percebidos pelo usuário.

Se a validação utilizar somente .length, alguns apelidos com emojis poderão ser rejeitados antes de atingir o limite visual.

Uma implementação mais adequada seria:

const segmentador = new Intl.Segmenter("pt-BR", {
granularity: "grapheme"
});

function validarNickname(nickname, limite = 15) {
const quantidade = [
  ...segmentador.segment(nickname)
].length;

return quantidade <= limite;
}

console.log(validarNickname("Sophie😄"));    // true
console.log(validarNickname("👩‍💻".repeat(16))); // false

Assim, um emoji composto pode ser contado como um único cluster de grafemas.

Se quisermos também limitar o texto automaticamente, podemos fazer:

function limitarTexto(texto, limite) {
const caracteres = Array.from(
  segmentador.segment(texto),
  item => item.segment
);

return caracteres.slice(0, limite).join("");
}

console.log(limitarTexto("Oi 👩‍💻!", 4));
// Oi 👩‍💻

Essa abordagem preserva os clusters de grafemas durante o corte, evitando separar componentes de um emoji composto.

Vale lembrar que nem todo limite deve ser baseado em grafemas. APIs, bancos de dados e protocolos podem estabelecer limites em bytes, pontos de código ou unidades UTF-16.

Antes de implementar a validação, é importante definir qual dessas medidas o sistema realmente exige.

5. Letras estilizadas também fazem parte dessa discussão

O Unicode não está presente apenas nos emojis.

Ele também aparece em símbolos matemáticos, caracteres especiais, alfabetos de diferentes idiomas e diversas formas de representação textual.

Por exemplo:

Texto convencional: Sophie

Texto estilizado: 𝑺𝒐𝒑𝒉𝒊𝒆

À primeira vista, parece que simplesmente mudamos a fonte.

Entretanto, nesse segundo exemplo, utilizamos caracteres Unicode diferentes das letras latinas convencionais. Não se trata apenas de aplicar uma propriedade CSS como font-family.

Essa diferença é especialmente relevante em ferramentas de personalização de perfis e textos.

Um exemplo desse tipo de aplicação é o Letras Diferentes, que permite explorar textos estilizados e símbolos Unicode para nomes, bios e perfis digitais.

Do ponto de vista do desenvolvimento, o interessante é compreender como esses caracteres se comportam quando são copiados, armazenados, pesquisados ou utilizados em formulários.

Nem sempre uma sequência de símbolos que parece simples visualmente possui uma representação igualmente simples no código.

Por isso, desenvolvedores precisam tomar cuidado ao implementar validações e funcionalidades relacionadas à manipulação de strings.

6. Um detalhe importante: dois textos iguais podem não ser idênticos

Existe outra particularidade do Unicode que merece atenção.

Duas palavras visualmente iguais podem ser representadas por sequências diferentes de pontos de código.

Veja este exemplo:

const texto1 = "é";
const texto2 = "e\u0301";

console.log(texto1 === texto2); // false

console.log(texto1.length); // 1
console.log(texto2.length); // 2

No primeiro caso, utilizamos o caractere é pré-composto.

No segundo, utilizamos a letra e seguida de um acento combinante.

Ambas as representações podem produzir o mesmo resultado visual, mas as strings não são idênticas.

Para lidar com equivalências canônicas, podemos utilizar o método normalize().

const texto1 = "é";
const texto2 = "e\u0301";

console.log(
texto1.normalize("NFC") === texto2.normalize("NFC")
); // true

O formato NFC é frequentemente utilizado para normalização canônica de textos.

Isso pode ser importante em buscas, comparação de conteúdo e processamento de dados.

Mas existe um cuidado: normalização Unicode não substitui validação de entrada, nem resolve automaticamente problemas de segurança relacionados a caracteres visualmente semelhantes.

7. E quanto à acessibilidade?

Esse assunto também está diretamente relacionado à experiência do usuário.

Imagine alguém preenchendo um campo com caracteres estilizados e recebendo uma mensagem informando que ultrapassou o limite permitido, apesar de enxergar um texto relativamente curto.

A validação pode estar tecnicamente correta para determinado sistema, mas a mensagem apresentada ao usuário talvez não explique o que realmente aconteceu.

Outro ponto importante envolve leitores de tela.

Caracteres Unicode estilizados podem ser interpretados de maneiras diferentes por tecnologias assistivas, dependendo dos símbolos e do software utilizado.

Por esse motivo, utilizar caracteres especiais apenas para criar uma aparência visual diferente nem sempre é a melhor solução para textos longos, instruções ou conteúdos essenciais.

Em interfaces web, quando o objetivo é somente alterar a aparência tipográfica, normalmente é preferível utilizar texto convencional acompanhado de CSS.

O Unicode continua sendo fundamental, mas precisa ser utilizado de maneira consciente.

8. Três cuidados antes de trabalhar com strings

Depois desses exemplos, vale guardar três recomendações:

1. Não utilize .length como sinônimo universal de caracteres visíveis.

Ele conta unidades UTF-16. Isso não é necessariamente equivalente à quantidade de símbolos que o usuário percebe.

2. Escolha a estratégia de contagem conforme a necessidade.

Para contar pontos de código, o spread operator pode ajudar. Para contar clusters de grafemas, considere Intl.Segmenter.

3. Teste entradas que fogem do alfabeto básico.

Inclua emojis compostos, caracteres com acentos combinantes, símbolos Unicode e diferentes sistemas de escrita.

Uma implementação que funciona perfeitamente com letras ASCII pode apresentar comportamentos inesperados diante de textos internacionais.

9. Conclusão

A propriedade .length parece simples, mas revela uma característica importante do desenvolvimento de software: nem sempre aquilo que vemos na interface corresponde diretamente à forma como os dados são representados internamente.

Compreender UTF-16, pontos de código e clusters de grafemas ajuda a desenvolver validações mais apropriadas, evitar cortes incorretos em strings e construir experiências melhores para pessoas que utilizam diferentes idiomas e símbolos.

O principal aprendizado não é abandonar .length.

É saber quando utilizá-la e quando precisamos de outra ferramenta.

No próximo formulário, editor de texto ou sistema de nicknames que você desenvolver, experimente executar esses exemplos no console.

Um simples emoji pode ensinar bastante sobre o funcionamento do JavaScript.

Referências

Sophie Ale

Criadora do Letras Diferentes | Tecnologia, Unicode e criatividade digital.

Compartir
Recomendado para ti
Bootcamp Bradesco - GenAI, Dados & Cyber
Bootcamp Afya - Automação de Dados com IA
Accenture - Python para Análise e Automação de Dados
Comentarios (0)