Strings são sequências de caracteres usadas para representar texto em JavaScript. Elas são um dos tipos primitivos mais importantes e estão presentes em praticamente todo programa. Nesta aula, vamos explorar desde a criação de strings até métodos avançados de manipulação, passando por template literals, imutabilidade e suporte a Unicode.

Dominar o trabalho com strings é essencial para qualquer desenvolvedor JavaScript, pois a maioria das aplicações lida com entrada de usuário, exibição de dados e formatação de texto. Vamos começar com o básico e progredir para tópicos mais profundos.

Criação e aspas

Em JavaScript, strings podem ser criadas usando aspas simples (' '), aspas duplas (" ") ou crases (` `). As três formas são equivalentes para strings simples, mas cada uma tem suas particularidades. Aspas simples e duplas são intercambiáveis, enquanto as crases permitem interpolação e strings multilinha.

É comum usar aspas simples como padrão para evitar conflitos com atributos HTML que usam aspas duplas. Para incluir uma aspa dentro da string, você pode escapar com barra invertida ou usar o outro tipo de aspa.

let nome = 'João';
let sobrenome = "Silva";
let frase = 'Ele disse: "Olá!"';
let caminho = "C:\\Users\\Nome";
console.log(nome, sobrenome, frase, caminho);

Para strings multilinha sem crases, é necessário usar \n ou concatenação. Com crases, a quebra de linha é natural.

let texto = "Linha 1\nLinha 2";
let textoCrase = `Linha 1
Linha 2`;
console.log(texto);
console.log(textoCrase);

Template literals

Template literals (ou template strings) são strings delimitadas por crases que oferecem recursos poderosos: interpolação de expressões com ${expressão}, strings multilinha e tags. Eles tornam a concatenação mais legível e evitam erros comuns.

A interpolação avalia qualquer expressão JavaScript dentro de ${}, permitindo inserir variáveis, chamadas de função e operações diretamente na string.

let nome = 'Maria';
let idade = 30;
let saudacao = `Olá, ${nome}! Você tem ${idade} anos.`;
console.log(saudacao); // Olá, Maria! Você tem 30 anos.

let preco = 19.99;
let quantidade = 3;
let total = `Total: R$ ${(preco * quantidade).toFixed(2)}`;
console.log(total); // Total: R$ 59.97

Template literals também permitem criar strings multilinha sem caracteres especiais, e podem ser usados com tags para processar a string (template tags).

let multilinha = `
  Linha 1
  Linha 2
  Linha 3
`;
console.log(multilinha);

Métodos essenciais

JavaScript oferece dezenas de métodos para strings. Aqui estão os mais importantes e usados no dia a dia.

  • length: propriedade que retorna o número de caracteres.
  • toUpperCase / toLowerCase: converte para maiúsculas/minúsculas.
  • indexOf / lastIndexOf: encontra a posição de uma substring.
  • slice(start, end): extrai parte da string.
  • substring(start, end): similar, mas não aceita índices negativos.
  • replace(search, replace): substitui a primeira ocorrência.
  • replaceAll(search, replace): substitui todas as ocorrências (ES2021).
  • split(separator): divide a string em array.
  • trim: remove espaços das extremidades.
  • includes, startsWith, endsWith: buscas booleanas.
  • repeat(n): repete a string n vezes.
  • padStart / padEnd: preenche a string até um comprimento.
let str = '  JavaScript é incrível! ';
console.log(str.length); // 23
console.log(str.toUpperCase()); //   JAVASCRIPT É INCRÍVEL! 
console.log(str.indexOf('incrível')); // 15
console.log(str.slice(2, 12)); // JavaScript
console.log(str.replace('incrível', 'poderoso')); //   JavaScript é poderoso! 
console.log(str.trim()); // JavaScript é incrível!
console.log('a,b,c'.split(',')); // ['a', 'b', 'c']
console.log('abc'.repeat(3)); // abcabcabc
console.log('5'.padStart(3, '0')); // 005

É importante notar que muitos métodos retornam uma nova string, pois strings são imutáveis (veja a próxima seção).

Imutabilidade

Strings em JavaScript são imutáveis, ou seja, uma vez criadas, seu conteúdo não pode ser alterado. Qualquer operação que pareça modificar a string na verdade retorna uma nova string. Isso é fundamental para entender o comportamento da linguagem.

Por exemplo, ao usar toUpperCase(), a string original permanece a mesma. Se você tentar atribuir um novo caractere a um índice, isso não funciona em modo estrito e gera erro em modo estrito.

let s = 'hello';
s[0] = 'H'; // não altera a string (silenciosamente ignorado em modo não estrito)
console.log(s); // 'hello'

// Para 'modificar', você deve criar uma nova string:
s = 'H' + s.slice(1);
console.log(s); // 'Hello'

Essa imutabilidade traz benefícios como segurança (strings não são alteradas acidentalmente) e otimizações de memória (strings idênticas podem ser compartilhadas).

Unicode básico

JavaScript usa internamente UTF-16 para representar strings. A maioria dos caracteres comuns (incluindo acentos e símbolos) é representada por uma única unidade de 16 bits (code unit). No entanto, caracteres especiais como emojis e alguns caracteres de idiomas asiáticos ocupam duas unidades (par surrogate).

Isso afeta métodos como length e charCodeAt. Para trabalhar corretamente com Unicode, use Array.from ou o operador spread para obter arrays de caracteres reais.

let emoji = '😀';
console.log(emoji.length); // 2 (porque é um par surrogate)
console.log([...emoji].length); // 1 (correto)

// Iteração correta:
for (let char of emoji) {
  console.log(char); // 😀
}

// Acessar ponto de código:
console.log(emoji.codePointAt(0)); // 128512

Para normalizar strings (por exemplo, 'é' pode ser representado como um caractere pré-composto ou como 'e' + acento), use normalize(). Isso é importante para comparações.

let s1 = '\u00E9'; // é pré-composto
let s2 = 'e\u0301'; // e + acento
console.log(s1 === s2); // false
console.log(s1.normalize() === s2.normalize()); // true

Boas práticas

  • Prefira template literals para concatenação e strings multilinha.
  • Use includes em vez de indexOf !== -1 para verificar existência.
  • Para substituições globais, use replaceAll ou replace com regex global.
  • Cuidado com a imutabilidade: sempre atribua o resultado a uma variável.
  • Ao trabalhar com Unicode, prefira iteração com for...of ou Array.from.

Referências

Exercícios

  1. Crie uma função que receba um nome e retorne uma saudação usando template literal. Exemplo: saudacao('Ana') retorna 'Olá, Ana!'.

    ✓ Resposta:
    function saudacao(nome) {
      return `Olá, ${nome}!`;
    }
    console.log(saudacao('Ana')); // Olá, Ana!
  2. Escreva uma função que receba uma string e retorne a mesma string com todas as vogais (a, e, i, o, u) substituídas por '*'. Use replace com regex ou replaceAll.

    ✓ Resposta:
    function ocultarVogais(str) {
      return str.replace(/[aeiou]/gi, '*');
    }
    console.log(ocultarVogais('JavaScript')); // J*v*Scr*pt
    
    // Alternativa com replaceAll:
    function ocultarVogais2(str) {
      const vogais = ['a', 'e', 'i', 'o', 'u'];
      vogais.forEach(v => str = str.replaceAll(v, '*'));
      return str;
    }
    console.log(ocultarVogais2('JavaScript')); // J*v*Scr*pt
  3. Dada a string ' Olá, mundo! ', remova os espaços das extremidades e converta para maiúsculas. Qual é o resultado?

    ✓ Resposta:
    let str = '   Olá, mundo!   ';
    let resultado = str.trim().toUpperCase();
    console.log(resultado); // 'OLÁ, MUNDO!'
  4. Explique por que o código abaixo não altera a string e como corrigi-lo para que a string se torne 'Hello':

    let s = 'hello';
    s[0] = 'H';
    console.log(s); // 'hello'

    ✓ Resposta:

    Strings são imutáveis; não é possível alterar um caractere por índice. Para obter 'Hello', crie uma nova string:

    let s = 'hello';
    s = 'H' + s.slice(1);
    console.log(s); // 'Hello'
  5. Escreva uma função que receba uma string e retorne um array com todos os caracteres reais (considerando pares surrogate). Use Array.from ou operador spread.

    ✓ Resposta:
    function paraArray(str) {
      return Array.from(str);
      // ou: return [...str];
    }
    console.log(paraArray('Olá 😀')); // ['O', 'l', 'á', ' ', '😀']
    console.log(paraArray('😀').length); // 1