Os tipos de dados em JavaScript
Esta aula explora os tipos de dados em JavaScript, divididos entre primitivos e objeto, com foco em suas particularidades, exemplos práticos e pegadinhas comuns como typeof null, NaN e coerção de tipos.
JavaScript é uma linguagem de tipagem dinâmica e fraca, o que significa que as variáveis podem conter valores de qualquer tipo e que a conversão entre tipos ocorre implicitamente em muitas operações. Nesta aula, vamos mergulhar nos tipos de dados que formam a base da linguagem: os primitivos e o tipo objeto. Entender esses tipos é fundamental para evitar bugs e escrever código previsível.
Além de conhecer cada tipo, veremos suas particularidades, exemplos de uso e as famosas "pegadinhas" que pegam até desenvolvedores experientes. Prepare-se para desvendar mistérios como typeof null, o comportamento de NaN e as regras da coerção de tipos.
Tipos primitivos e objeto
Em JavaScript, os tipos de dados são classificados em duas categorias: tipos primitivos e o tipo objeto. Os primitivos são imutáveis e não possuem métodos próprios (embora pareçam ter, por causa do autoboxing). São eles: string, number, bigint, boolean, undefined, symbol e null (este último é um primitivo, mas typeof retorna "object" — uma pegadinha histórica).
Já o tipo objeto é uma coleção de propriedades, mutável e com identidade. Inclui objetos simples, arrays, funções, datas, expressões regulares, etc. Praticamente tudo que não é primitivo é um objeto. A diferença fundamental é que objetos são armazenados por referência, enquanto primitivos são armazenados por valor.
// Primitivos: armazenados por valor
let a = 10;
let b = a;
b = 20;
console.log(a); // 10 (não foi alterado)
// Objetos: armazenados por referência
let obj1 = { valor: 10 };
let obj2 = obj1;
obj2.valor = 20;
console.log(obj1.valor); // 20 (foi alterado)
Particularidades de cada tipo
String: Sequência de caracteres Unicode. Imutável: qualquer operação que pareça modificar a string retorna uma nova string. Pode ser delimitada por aspas simples, duplas ou crases (template literals).
let nome = "João";
console.log(nome[0]); // "J"
nome[0] = "P"; // não altera, strings são imutáveis
console.log(nome); // "João"
Number: Representa números de ponto flutuante (IEEE 754). Inclui valores especiais: Infinity, -Infinity e NaN (Not-a-Number). Não há distinção entre inteiros e decimais.
console.log(1 / 0); // Infinity
console.log(-1 / 0); // -Infinity
console.log(0 / 0); // NaN
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
BigInt: Para inteiros de precisão arbitrária. Criado adicionando n ao final do número. Não pode ser misturado com number em operações aritméticas.
const big = 123456789012345678901234567890n;
console.log(big + 1n); // 123456789012345678901234567891n
Boolean: Apenas true e false. Usado em condicionais. Qualquer valor pode ser convertido para booleano (truthy/falsy).
console.log(Boolean(0)); // false
console.log(Boolean("")); // false
console.log(Boolean("texto")); // true
Undefined: Valor atribuído a variáveis declaradas mas não inicializadas. Também é o retorno de funções sem return explícito.
let x;
console.log(x); // undefined
Null: Representa a ausência intencional de um valor. É um primitivo, mas typeof null retorna "object" devido a um bug histórico.
let y = null;
console.log(typeof y); // "object" (pegadinha!)
Symbol: Valor único e imutável, frequentemente usado como chave de propriedade de objeto para evitar colisões.
const sym1 = Symbol("descricao");
const sym2 = Symbol("descricao");
console.log(sym1 === sym2); // false
Objeto: Coleção de pares chave-valor. Pode conter funções (métodos). Arrays e funções são subtipos de objeto.
let pessoa = { nome: "Maria", idade: 30 };
console.log(pessoa.nome); // "Maria"
Exemplos
Vamos ver exemplos práticos que ilustram o comportamento de cada tipo em operações comuns.
// String e Number: concatenação vs soma
console.log("5" + 3); // "53" (coerção para string)
console.log("5" - 3); // 2 (coerção para number)
// Boolean em contexto numérico
console.log(true + 1); // 2 (true vira 1)
console.log(false + 1); // 1 (false vira 0)
// Undefined e null em operações
console.log(undefined + 1); // NaN
console.log(null + 1); // 1 (null vira 0)
// Comparações estritas vs soltas
console.log(0 == false); // true (coerção)
console.log(0 === false); // false (tipos diferentes)
Pegadinhas (typeof null, NaN, coerção)
typeof null: Como mencionado, typeof null retorna "object". Isso é um erro de implementação que não foi corrigido para não quebrar código legado. Para verificar se um valor é null, use valor === null.
NaN: NaN é o único valor em JavaScript que não é igual a si mesmo. NaN === NaN é false. Para verificar se um valor é NaN, use Number.isNaN() (ES6) ou isNaN() (que faz coerção).
console.log(NaN === NaN); // false
console.log(Number.isNaN(NaN)); // true
console.log(isNaN("texto")); // true (coerção: Number("texto") é NaN)
console.log(Number.isNaN("texto")); // false (não faz coerção)
Coerção de tipos: JavaScript converte automaticamente tipos em operações. Isso pode levar a resultados inesperados. Exemplos clássicos:
console.log([] + []); // "" (arrays viram strings vazias e concatenam)
console.log([] + {}); // "[object Object]"
console.log({} + []); // 0 (em alguns contextos, {} é interpretado como bloco vazio)
console.log(1 + "2" + "2"); // "122"
console.log(1 + +"2" + "2"); // "32" (o unário + converte "2" para number)
console.log("A" - "B" + "2"); // "NaN2" ("A"-"B" é NaN, depois concatena com "2")
Boas práticas
Para evitar surpresas, prefira sempre a comparação estrita (=== e !==) em vez da solta (== e !=). Use Number.isNaN() em vez de isNaN() para verificar NaN sem coerção. E lembre-se: null deve ser verificado com === null, não com typeof.
Referências
- MDN: Estruturas de dados em JavaScript
- MDN: NaN
- MDN: typeof
- MDN: Comparações de igualdade
- MDN: Symbol
- MDN: BigInt
Exercícios
Qual é o resultado de
typeof null? Por que isso acontece?✓ Resposta:typeof nullretorna"object". Isso é um erro de implementação no JavaScript desde sua criação, que não foi corrigido para não quebrar código legado. Para verificar null, use=== null.Explique por que
NaN === NaNéfalsee como verificar se um valor é NaN corretamente.✓ Resposta:NaN(Not-a-Number) é o único valor em JavaScript que não é igual a si mesmo, conforme especificação IEEE 754. Para verificar corretamente, useNumber.isNaN(valor)(ES6) que não faz coerção, ouisNaN(valor)que faz coerção para number antes de verificar.Qual a diferença entre
==e===? Dê um exemplo onde o resultado difere.✓ Resposta:==(igualdade solta) faz coerção de tipos antes da comparação;===(igualdade estrita) não faz coerção, comparando valor e tipo. Exemplo:0 == falseretornatrue(pois false é convertido para 0), enquanto0 === falseretornafalse(tipos diferentes).O que é autoboxing em JavaScript? Dê um exemplo.
✓ Resposta: Autoboxing é o processo automático pelo qual um primitivo é temporariamente convertido em seu objeto wrapper correspondente para acessar métodos. Exemplo:"texto".toUpperCase()— a string primitiva é convertida temporariamente em um objeto String para chamar o métodotoUpperCase().Qual o resultado de
1 + "2" + "2"e de1 + +"2" + "2"? Explique.✓ Resposta:1 + "2" + "2"resulta em"122": o primeiro+é concatenação (número + string), gerando"12", depois concatena com"2". Já1 + +"2" + "2"resulta em"32": o operador unário+converte"2"para número 2, então1 + 2 = 3(número), depois3 + "2"vira"32"(concatenação).