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()ounew obj?.(). - Com template literals no lado direito:
objeto?.propriedade``. - Com
super:super?.metodo()ousuper?.propriedade. - No lado esquerdo de uma atribuição:
obj?.propriedade = valor(isso geraria um erro de sintaxe, pois o resultado deobj?.propriedadepode serundefined, e você não pode atribuir aundefined).
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)
- 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)
- Atribuição de Coalescência Nula (
??=): Atribui o valor do operando à direita ao operando à esquerda se o operando à esquerda fornullouundefined.
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.