Operadores são fundamentais em qualquer linguagem de programação, pois permitem realizar operações sobre valores e variáveis. Em JavaScript, os operadores são usados para cálculos aritméticos, comparações, lógica booleana, atribuições e manipulações mais avançadas. Nesta aula, exploraremos os principais tipos de operadores, com destaque para nuances importantes como a diferença entre igualdade solta e estrita, e os operadores modernos introduzidos no ES6 e posteriores.

Dominar os operadores é essencial para escrever código expressivo, conciso e correto. Vamos mergulhar em cada categoria com exemplos práticos.

Aritméticos

Os operadores aritméticos são usados para realizar operações matemáticas em números. JavaScript fornece os operadores básicos: adição (+), subtração (-), multiplicação (*), divisão (/) e o resto da divisão (%). Além disso, existem os operadores de incremento (++) e decremento (--), que são unários.

Importante notar que o operador + também é usado para concatenação de strings. Se um dos operandos for uma string, o outro é convertido em string e concatenado. Já os demais operadores aritméticos tentam converter strings em números.

// Operadores aritméticos básicos
let a = 10;
let b = 3;
console.log(a + b); // 13
console.log(a - b); // 7
console.log(a * b); // 30
console.log(a / b); // 3.333...
console.log(a % b); // 1 (resto)

// Incremento e decremento
let contador = 0;
contador++; // pós-incremento: retorna 0, depois incrementa
console.log(contador); // 1
++contador; // pré-incremento: incrementa e retorna 2
console.log(contador); // 2

// Concatenação com +
console.log(5 + '5'); // "55" (string)
console.log('5' - 3); // 2 (número, pois - força conversão)

Comparação (== vs ===)

Operadores de comparação retornam um valor booleano (true ou false). Os principais são: igualdade solta (==), igualdade estrita (===), desigualdade solta (!=), desigualdade estrita (!==), maior (>), menor (<), maior ou igual (>=) e menor ou igual (<=).

A diferença crucial entre == e === é que == realiza coerção de tipo antes da comparação, enquanto === compara valor e tipo sem coerção. É recomendado usar === sempre para evitar resultados inesperados.

// Exemplos de comparação
console.log(5 == '5');  // true (coerção: string '5' vira número 5)
console.log(5 === '5'); // false (tipos diferentes)
console.log(0 == false); // true (false é convertido para 0)
console.log(0 === false); // false
console.log(null == undefined); // true (regra especial)
console.log(null === undefined); // false

// Comparações numéricas
console.log(10 > 5);  // true
console.log(10 <= 10); // true

Lógicos

Operadores lógicos combinam expressões booleanas: && (AND lógico), || (OR lógico) e ! (NOT lógico). Eles são frequentemente usados em condicionais e também podem ser usados para atribuição de valores com curto-circuito.

No JavaScript, os operadores lógicos funcionam com curto-circuito: no &&, se o primeiro operando for falsy, o segundo não é avaliado; no ||, se o primeiro for truthy, o segundo é ignorado. Eles retornam o valor de um dos operandos (não necessariamente booleano).

// Operadores lógicos
let idade = 25;
let temCarteira = true;

// AND lógico
if (idade >= 18 && temCarteira) {
  console.log('Pode dirigir');
}

// OR lógico
let nome = '';
let apelido = 'João';
let nomeExibido = nome || apelido; // 'João'
console.log(nomeExibido);

// NOT lógico
let ativo = false;
console.log(!ativo); // true

// Curto-circuito com &&
let usuario = { nome: 'Maria' };
usuario && console.log(usuario.nome); // 'Maria'

Operadores modernos (??, ?., spread/rest)

O operador de coalescência nula (??) retorna o operando da direita se o da esquerda for null ou undefined, caso contrário retorna o da esquerda. Diferente do ||, ele não considera outros valores falsy como 0 ou '' como nulos.

O operador de encadeamento opcional (?.) permite acessar propriedades de um objeto de forma segura, sem lançar erro se uma propriedade intermediária for null ou undefined. Spread (...) expande arrays/objetos, enquanto rest (...) agrupa argumentos em um array.

// Coalescência nula (??)
let valor = 0;
let resultado = valor ?? 'padrão'; // 0 (porque 0 não é null/undefined)
console.log(resultado);

let nulo = null;
let resultado2 = nulo ?? 'padrão'; // 'padrão'
console.log(resultado2);

// Encadeamento opcional (?.)
const pessoa = { endereco: { rua: 'Rua A' } };
console.log(pessoa.endereco?.rua);   // 'Rua A'
console.log(pessoa.contato?.email); // undefined (sem erro)

// Spread com arrays
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combinado = [...arr1, ...arr2]; // [1,2,3,4,5,6]

// Rest em parâmetros
function somar(...numeros) {
  return numeros.reduce((acc, n) => acc + n, 0);
}
console.log(somar(1, 2, 3)); // 6

Precedência

Precedência de operadores determina a ordem em que as operações são avaliadas. Operadores com maior precedência são avaliados primeiro. Por exemplo, multiplicação tem precedência maior que adição. Parênteses podem ser usados para forçar uma ordem específica.

É importante conhecer a precedência para evitar bugs. Uma tabela completa está disponível na MDN. Aqui estão alguns exemplos:

// Precedência: multiplicação antes de adição
let resultado = 2 + 3 * 4; // 14 (3*4=12, +2=14)
console.log(resultado);

// Parênteses alteram a ordem
let resultado2 = (2 + 3) * 4; // 20
console.log(resultado2);

// Operadores lógicos: ! tem maior precedência, depois &&, depois ||
let a = true;
let b = false;
let c = true;
console.log(a || b && c); // true (b && c é false, a || false = true)
console.log((a || b) && c); // true (a || b = true, true && c = true)

Boas práticas e observações finais

Sempre prefira === e !== em vez de == e != para evitar coerção implícita. Use ?? para valores nulos/undefined, e ?. para acessos seguros. Use parênteses para tornar a precedência explícita e melhorar a legibilidade. Evite cadeias muito longas de operadores lógicos sem parênteses.

Referências

Exercícios

  1. Qual é a saída do código: console.log(10 + '10');? Explique.
  2. ✓ Resposta: A saída é a string "1010". O operador +, quando um dos operandos é uma string, realiza concatenação, convertendo o número 10 em string.
  3. Diferencie == e === com um exemplo prático.
  4. ✓ Resposta: Exemplo: 5 == '5' retorna true (coerção de tipo), enquanto 5 === '5' retorna false (tipos diferentes).
  5. O que o operador ?? retorna no código: let x = 0 ?? 'padrão';? Por quê?
  6. ✓ Resposta: Retorna 0, pois ?? só considera null/undefined como nulos. 0 é um valor válido (falsy, mas não nulo).
  7. Usando encadeamento opcional, como acessar a propriedade email de um objeto user que pode ser null sem causar erro?
  8. ✓ Resposta: user?.email retorna undefined se user for null/undefined, sem lançar erro.
  9. Qual a ordem de avaliação da expressão true || false && false? Use parênteses para tornar explícito.
  10. ✓ Resposta: A precedência do && é maior que a do ||, então a expressão é avaliada como true || (false && false) => true || false => true.