Operadores Estendidos no JavaScript

Operador de Exponenciação (**)

O JavaScript, a partir do ES2016, introduziu o operador de exponenciação (**), que realiza a potência de um número. Diferente de muitos operadores binários que são associativos à esquerda, este operador possui associatividade à direita. Isso significa que, em uma sequência de operações de exponenciação, a avaliação ocorre da direita para a esquerda.


// 3 elevado à potência de 4
console.log(3 ** 4); // 81

// 2 elevado à potência de 3 elevado à potência de 2
// Equivalente a 2 ** (3 ** 2) => 2 ** 9
console.log(2 ** 3 ** 2); // 512

O operador de exponenciação também pode ser combinado com o operador de atribuição, formando **=.


let baseNumero = 5;
baseNumero **= 3; // Equivalente a baseNumero = baseNumero ** 3;
console.log(baseNumero); // 125 (5 * 5 * 5)

let resultado = 2;
resultado **= 4; // Equivalente a resultado = resultado ** 4;
console.log(resultado); // 16 (2 * 2 * 2 * 2)

Operador de Encadeamento Opcional (?.)

No desenvolvimento JavaScript, é comum acessar propriedades de objetos aninhados. No entanto, se um nível intermediário da cadeia de propriedades for null ou undefined, a tentativa de acessar a próxima propriedade resultará em um erro. Tradicionalmente, isso exigia verificações condicionais explícitas para cada nível da hierarquia.


// Cenário sem o operador ?.
const perfil = {
  usuario: {
    nome: 'Alice',
    contato: {
      email: 'alice@example.com'
    }
  }
};

// Acessando um campo aninhado de forma segura (antiga)
let telefoneContato;
if (perfil && perfil.usuario && perfil.usuario.contato) {
  telefoneContato = perfil.usuario.contato.telefone; // 'telefone' não existe, seria undefined
} else {
  telefoneContato = undefined;
}
console.log(telefoneContato); // undefined

Para simplificar essas verificações, o ES2020 introduziu o operador de encadeamento opcional (?.). Ele permite que você leia o valor de uma propriedade localizada profundamente dentro de uma cadeia de objetos sem a necessidade de validar explicitamente cada referência na cadeia. Se uma referência for null ou undefined, a expressão inteira retorna undefined e não lança um erro.


const dadosProduto = {
  informacoes: {
    nome: 'Smartphone X',
    especificacoes: {
      processador: 'Snapdragon',
      memoria: '8GB'
    }
  }
};

// Usando o operador de encadeamento opcional
const dimensao = dadosProduto?.informacoes?.especificacoes?.dimensoes;
console.log(dimensao); // undefined (sem erro, pois 'dimensoes' não existe)

const processador = dadosProduto?.informacoes?.especificacoes?.processador;
console.log(processador); // Snapdragon

// Com elemento de formulário
const campoBusca = document.querySelector('input[name="search"]')
const valorBusca = campoBusca?.value; // Retorna o valor se o campo existir, ou undefined

O ?. também é útil para chamar métodos que podem não existir. Se a propriedade à esquerda do ?. for uma função e existir, ela será executada; caso contrário, a expressão retorna undefined.


const servi = {
  logErro: (msg) => console.error(msg)
};

const serviSemLog = {};

// Chama logErro se existir
servi.logErro?.('Ocorreu um erro crítico!'); // Executa

// Não faz nada se a função não existir
serviSemLog.logErro?.('Erro menos grave, ignorado.'); // Não executa

As três formas principais de usar o operador de encadeamento opcional são:

  • objeto?.propriedade: Acessa uma propriedade de objeto.
  • objeto?.[expressao]: Acessa uma propriedade de objeto usando uma expressão (como um nome de chave dinâmico ou índice de array).
  • funcao?.(argumentos): Chama uma função ou método.

let listaDeTags = "cores:azul,verde,amarelo".match(/cores:([a-z,]+)/i)?.[1];
console.log(listaDeTags); // "azul,verde,amarelo"

// Se não houver correspondência, match retorna null, e o ?. garante que [1] não seja acessado.
let listaInexistente = "semcores".match(/cores:([a-z,]+)/i)?.[1];
console.log(listaInexistente); // undefined

Considerações Importantes sobre o ?.

1. Mecanismo de Curto-circuito:

O operador ?. implementa um mecanismo de curto-circuito. Se a parte da expressão antes do ?. for null ou undefined, a avaliação da expressão é interrompida imediatamente, e undefined é retornado. Isso significa que quaisquer efeitos colaterais (como incremento de variáveis) na parte direita do ?. não ocorrerão.


let contador = 0;
const objVazio = null;
const resultado = objVazio?.['prop' + ++contador];
console.log(resultado); // undefined
console.log(contador); // 0 (o contador não foi incrementado)

2. Impacto de Parênteses:

Se uma expressão com ?. estiver dentro de parênteses, o ?. afeta apenas o que está dentro deles. O que está fora dos parênteses será avaliado independentemente, o que pode levar a erros se a parte avaliada fora dos parênteses não for definida.


const dados = { item: null };
// (dados?.item).propriedade vai falhar porque (dados?.item) é undefined, e undefined.propriedade causa erro.
// console.log((dados?.item).propriedade); // Erro: Cannot read properties of null (reading 'propriedade')

Geralmente, evite usar parênteses em expressões com ?. a menos que seja para controlar a precedência de outras operações.

3. Usos Proibidos:

O operador de encadeamento opcional não pode ser usado em algumas situações:

  • Com o operador new: new obj?.Metodo() ou new obj?.().
  • Com template literals no lado direito: objeto?.propriedade``.
  • Com super: super?.metodo() ou super?.propriedade.
  • No lado esquerdo de uma atribuição: obj?.propriedade = valor (isso geraria um erro de sintaxe, pois o resultado de obj?.propriedade pode ser undefined, e você não pode atribuir a undefined).

4. Após um Ponto Decimal:

Para manter a compatibilidade com o código legado que pode usar o ponto decimal como parte de um número decimal, se ?. for imediatamente seguido por um dígito decimal, ele não será interpretado como um operador de encadeamento opcional. Em vez disso, o ponto será considerado parte de um número decimal.


// foo?.3:0 é interpretado como foo ? .3 : 0 (um operador ternário)
// e não como (foo?.3):0

Essa é uma situação rara, mas é importante estar ciente para evitar ambiguidades.

Operador de Coalescência Nula (??)

Ao definir valores padrão para variáveis ou propriedades de objetos, o operador lógico OR (||) é frequentemente usado. No entanto, o || tem um comportamento que pode não ser o desejado: ele retorna o operando à direita se o operendo à esquerda for "falsey" (qualquer valor que se avalia como false em um contexto booleano), o que inclui 0, '' (string vazia), false, null e undefined.


const configuracoesPadrao = {
  nomeApp: null,
  maxTentativas: 0,
  habilitarNotificacoes: false
};

// Usando || - pode não ser o esperado
const nomeExibicao = configuracoesPadrao.nomeApp || 'Meu Aplicativo';
const tentativasPermitidas = configuracoesPadrao.maxTentativas || 5;
const notificarUsuario = configuracoesPadrao.habilitarNotificacoes || true;

console.log(nomeExibicao);         // "Meu Aplicativo" (nomeApp é null, então usa o padrão)
console.log(tentativasPermitidas); // 5 (maxTentativas é 0, que é falsey, então usa o padrão) - INCORRETO se 0 for um valor válido
console.log(notificarUsuario);     // true (habilitarNotificacoes é false, que é falsey, então usa o padrão) - INCORRETO se false for um valor válido

Para resolver essa questão, o ES2020 introduziu o operador de coalescência nula (??). Ele retorna o operendo à direita apenas se o operando à esquerda for estritamente null ou undefined. Para todos os outros valores (incluindo 0, '' e false), ele retorna o operando à esquerda.


const configuracoesAtuais = {
  nomeApp: 'Painel Admin',
  maxTentativas: 0,
  habilitarNotificacoes: false,
  tema: null
};

// Usando ?? - comportamento correto
const nomeExibicaoNovo = configuracoesAtuais.tema ?? 'Tema Padrão';
const tentativasPermitidasNovo = configuracoesAtuais.maxTentativas ?? 5;
const notificarUsuarioNovo = configuracoesAtuais.habilitarNotificacoes ?? true;

console.log(nomeExibicaoNovo);         // "Tema Padrão" (tema é null, usa o padrão)
console.log(tentativasPermitidasNovo); // 0 (maxTentativas é 0, que NÃO é null/undefined, então usa 0) - CORRETO
console.log(notificarUsuarioNovo);     // false (habilitarNotificacoes é false, que NÃO é null/undefined, então usa false) - CORRETO

O operador ?? se harmoniza bem com o operador de encadeamento opcional ?., permitindo definir um valor padrão para propriedades que podem não existir ou serem nulas/indefinidas em uma cadeia.


const userSettings = {
  preferencias: {
    idioma: 'pt-BR'
    // tempoLimiteSessao não definido
  }
};

const idiomaAtual = userSettings?.preferencias?.idioma ?? 'en-US';
console.log(idiomaAtual); // pt-BR

const tempoLimite = userSettings?.preferencias?.tempoLimiteSessao ?? 1800;
console.log(tempoLimite); // 1800 (pois tempoLimiteSessao é undefined)

É uma ferramenta excelente para validar parâmetros de funções, garantindo que eles tenham um valor padrão apenas se não forem explicitamente fornecidos como null ou undefined.


function exibirMensagem(texto, cor = null) {
  const corPadrao = cor ?? 'preto'; // Se cor for null/undefined, usa 'preto'
  console.log(`<span style="color: ${corPadrao}">${texto}</span>`);
}

exibirMensagem('Olá Mundo', 'azul');   // Saída: <span style="color: azul">Olá Mundo</span>
exibirMensagem('Aviso Importante');  // Saída: <span style="color: preto">Aviso Importante</span> (cor é null por padrão, então usa 'preto')
exibirMensagem('Mensagem em Vermelho', 'vermelho'); // Saída: <span style="color: vermelho">Mensagem em Vermelho</span>
exibirMensagem('Conteúdo com 0', 0); // Saída: <span style="color: 0">Conteúdo com 0</span> (0 é considerado um valor válido)

Precedência com Outros Operadores Lógicos

O ?? tem uma precedência específica em relação aos outros operadores lógicos (&& e ||). Para evitar ambiguidades e erros, o JavaScript exige que você use parênteses se for combinar ?? com && ou || na mesma expressão.


// Estas expressões resultarão em erro de sintaxe se os parênteses não forem usados:
// a && b ?? c;
// x ?? y || z;

// Formas corretas com parênteses:
console.log((true && undefined) ?? 'Valor A'); // Valor A
console.log(true && (undefined ?? 'Valor B')); // Valor B

console.log((false || undefined) ?? 'Valor C'); // Valor C
console.log(false || (undefined ?? 'Valor D')); // Valor D

Operadores de Atribuição Lógica

O ES2021 introduziu três operadores de atribuição lógica que combinam operdaores lógicos com o operador de atribuição (=). Eles oferecem uma sintaxe mais concisa para realizar atribuições condicionais com base em um valor lógico.

  • Atribuição OR (||=): Atribui o valor do operando à direita ao operando à esquerda se o operando à esquerda for "falsey".

  let config = { limite: 0, habilitado: false, nome: null };

  // Equivalente a: config.limite = config.limite || 10;
  config.limite ||= 10;
  console.log(config.limite); // 10 (0 é falsey)

  // Equivalente a: config.habilitado = config.habilitado || true;
  config.habilitado ||= true;
  console.log(config.habilitado); // true (false é falsey)

  // Equivalente a: config.nome = config.nome || 'Visitante';
  config.nome ||= 'Visitante';
  console.log(config.nome); // Visitante (null é falsey)
  
  1. Atribuição AND (&&=): Atribui o valor do operando à direita ao operando à esquerda se o operando à esquerda for "truthy".

 let permissoes = { admin: true, editor: false };

 // Equivalente a: permissoes.admin = permissoes.admin && true;
 permissoes.admin &&= 'Acesso Total';
 console.log(permissoes.admin); // Acesso Total (true é truthy)

 // Equivalente a: permissoes.editor = permissoes.editor && 'Editar Conteúdo';
 permissoes.editor &&= 'Editar Conteúdo';
 console.log(permissoes.editor); // false (false é falsey, então não atribui)
 
  1. Atribuição de Coalescência Nula (??=): Atribui o valor do operando à direita ao operando à esquerda se o operando à esquerda for null ou undefined.

 let preferencias = { tema: null, notificacoes: undefined, modoEscuro: false };

 // Equivalente a: preferencias.tema = preferencias.tema ?? 'claro';
 preferencias.tema ??= 'claro';
 console.log(preferencias.tema); // claro (null)

 // Equivalente a: preferencias.notificacoes = preferencias.notificacoes ?? 'ativadas';
 preferencias.notificacoes ??= 'ativadas';
 console.log(preferencias.notificacoes); // ativadas (undefined)

 // Equivalente a: preferencias.modoEscuro = preferencias.modoEscuro ?? true;
 preferencias.modoEscuro ??= true;
 console.log(preferencias.modoEscuro); // false (false não é null/undefined)
 

Esses operadores são particularmente úteis para configurar valores padrão de forma concisa ou para modificar valores existentes sob certas condições lógicas, reduzindo a verbosidade do código.

Tags: javascript ES2016 ES2020 ES2021 Operadores JavaScript

Publicado em 7-12 10:31