Uma adição recente
Esta é uma adição recente ao idioma. Navegadores antigos podem precisar de polyfills.
O encadeamento opcional ?.
é uma maneira segura de acessar propriedades de objetos aninhados, mesmo que não exista uma propriedade intermediária.
Se você acabou de começar a ler o tutorial e aprender JavaScript, talvez o problema ainda não tenha afetado você, mas é bastante comum.
Por exemplo, digamos que temos objetos user
que contêm informações sobre nossos usuários.
A maioria dos nossos usuários possui endereços na propriedade user.address
, com a rua user.address.street
, mas alguns não os forneceram.
Nesse caso, quando tentamos obter user.address.street
e o usuário está sem endereço, obtemos um erro:
deixe usuário = {}; // um usuário sem propriedade "address" alerta(usuário.endereço.rua); // Erro!
Esse é o resultado esperado. JavaScript funciona assim. Como user.address
é undefined
, uma tentativa de obter user.address.street
falha com um erro.
Em muitos casos práticos, preferiríamos ficar undefined
em vez de um erro aqui (que significa “sem rua”).
…e outro exemplo. No desenvolvimento Web, podemos obter um objeto que corresponde a um elemento de página web usando uma chamada de método especial, como document.querySelector('.elem')
, e retorna null
quando não existe tal elemento.
// document.querySelector('.elem') é nulo se não houver elemento deixe html = document.querySelector('.elem').innerHTML; //erro se for nulo
Mais uma vez, se o elemento não existir, obteremos um erro ao acessar a propriedade .innerHTML
de null
. E em alguns casos, quando a ausência do elemento é normal, gostaríamos de evitar o erro e apenas aceitar html = null
como resultado.
Como podemos fazer isso?
A solução óbvia seria verificar o valor usando if
ou o operador condicional ?
, antes de acessar sua propriedade, assim:
deixe usuário = {}; alerta (user.address? user.address.street: indefinido);
Funciona, não tem erro… Mas é bastante deselegante. Como você pode ver, "user.address"
aparece duas vezes no código.
Veja como seria o mesmo para document.querySelector
:
deixe html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML: null;
Podemos ver que o elemento search document.querySelector('.elem')
é na verdade chamado duas vezes aqui. Não é bom.
Para propriedades aninhadas mais profundamente, torna-se ainda mais feio, pois são necessárias mais repetições.
Por exemplo, vamos obter user.address.street.name
de maneira semelhante.
deixe usuário = {}; //usuário não tem endereço alerta (user.address? user.address.street? user.address.street.name: null: null);
Isso é horrível, pode até ter problemas para entender esse código.
Existe uma maneira um pouco melhor de escrevê-lo, usando o operador &&
:
deixe usuário = {}; //usuário não tem endereço alerta(user.address && user.address.street && user.address.street.name ); // indefinido (sem erro)
Andar todo o caminho até a propriedade garante que todos os componentes existam (caso contrário, a avaliação será interrompida), mas também não é o ideal.
Como você pode ver, os nomes das propriedades ainda estão duplicados no código. Por exemplo, no código acima, user.address
aparece três vezes.
É por isso que o encadeamento opcional ?.
foi adicionado ao idioma. Para resolver esse problema de uma vez por todas!
O encadeamento opcional ?.
interrompe a avaliação se o valor antes de ?.
é undefined
ou null
e retorna undefined
.
Mais adiante neste artigo, por questões de brevidade, diremos que algo “existe” se não for null
e não undefined
.
Em outras palavras, value?.prop
:
funciona como value.prop
, se value
existir,
caso contrário (quando value
é undefined/null
), ele retorna undefined
.
Esta é a maneira segura de acessar user.address.street
usando ?.
:
deixe usuário = {}; //usuário não tem endereço alerta(usuário?.endereço?.rua); // indefinido (sem erro)
O código é curto e limpo, não há duplicação alguma.
Aqui está um exemplo com document.querySelector
:
deixe html = document.querySelector('.elem')?.innerHTML; // será indefinido, se não houver elemento
Ler o endereço com user?.address
funciona mesmo se o objeto user
não existir:
deixe usuário = null; alerta(usuário?.endereço); // indefinido alerta(usuário?.endereço.rua); // indefinido
Observação: o ?.
a sintaxe torna opcional o valor anterior, mas não mais.
Por exemplo, em user?.address.street.name
o ?.
permite que user
seja null/undefined
com segurança (e retorne undefined
nesse caso), mas isso é apenas para user
. Outras propriedades são acessadas regularmente. Se quisermos que alguns deles sejam opcionais, precisaremos substituir more .
com ?.
.
Não abuse do encadeamento opcional
Devemos usar ?.
somente onde está tudo bem que algo não exista.
Por exemplo, se de acordo com nossa lógica de código o objeto user
deve existir, mas address
é opcional, então devemos escrever user.address?.street
, mas não user?.address?.street
.
Então, se user
for indefinido, veremos um erro de programação e o corrigiremos. Caso contrário, se usarmos demais ?.
, os erros de codificação podem ser silenciados quando não for apropriado e tornar-se mais difíceis de depurar.
A variável antes de ?.
deve ser declarado
Se não houver nenhuma variável user
, então user?.anything
dispara um erro:
// ReferenceError: usuário não está definido usuário?.endereço;
A variável deve ser declarada (por exemplo, let/const/var user
ou como parâmetro de função). O encadeamento opcional funciona apenas para variáveis declaradas.
Como foi dito antes, o ?.
interrompe imediatamente (“curta-circuito”) a avaliação se a parte esquerda não existir.
Portanto, se houver outras chamadas de função ou operações à direita de ?.
, eles não serão feitos.
Por exemplo:
deixe usuário = null; seja x = 0; usuário?.sayHi(x++); // sem "usuário", então a execução não alcança a chamada sayHi e x++ alerta(x); // 0, valor não incrementado
O encadeamento opcional ?.
não é um operador, mas uma construção de sintaxe especial, que também funciona com funções e colchetes.
Por exemplo, ?.()
é usado para chamar uma função que pode não existir.
No código abaixo, alguns de nossos usuários possuem o método admin
e outros não:
deixe userAdmin = { administrador() { alerta("Eu sou administrador"); } }; deixe userGuest = {}; userAdmin.admin?.(); //Eu sou administrador userGuest.admin?.(); // nada acontece (não existe tal método)
Aqui, em ambas as linhas, primeiro usamos o ponto ( userAdmin.admin
) para obter a propriedade admin
, porque assumimos que o objeto user
existe, portanto, é seguro lê-lo.
Então ?.()
verifica a parte esquerda: se a função admin
existir, ela será executada (isso é verdade para userAdmin
). Caso contrário (para userGuest
) a avaliação será interrompida sem erros.
A sintaxe ?.[]
também funciona, se quisermos usar colchetes []
para acessar propriedades em vez de dot .
. Semelhante aos casos anteriores, permite ler com segurança uma propriedade de um objeto que pode não existir.
deixe chave = "primeiroNome"; deixe usuário1 = { primeiroNome: "João" }; deixe usuário2 = nulo; alerta(usuário1?.[chave]); // John alerta(usuário2?.[chave]); // indefinido
Também podemos usar ?.
com delete
:
excluir usuário?.nome; //exclui user.name se o usuário existir
Podemos usar ?.
para leitura e exclusão seguras, mas não para gravação
O encadeamento opcional ?.
não tem utilidade no lado esquerdo de uma tarefa.
Por exemplo:
deixe usuário = null; usuário?.nome = "João"; //Erro, não funciona // porque é avaliado como: undefined = "John"
O encadeamento opcional ?.
a sintaxe tem três formas:
obj?.prop
– retorna obj.prop
se obj
existir, caso contrário, undefined
.
obj?.[prop]
– retorna obj[prop]
se obj
existir, caso contrário, undefined
.
obj.method?.()
– chama obj.method()
se obj.method
existir, caso contrário, retorna undefined
.
Como podemos ver, todos eles são diretos e simples de usar. ?.
verifica se null/undefined
na parte esquerda e permite que a avaliação prossiga se não for assim.
Uma cadeia de ?.
permite acessar com segurança propriedades aninhadas.
Ainda assim, devemos aplicar ?.
com cuidado, apenas onde for aceitável, de acordo com a nossa lógica de código, que a parte esquerda não exista. Para que não nos esconda erros de programação, caso ocorram.