Una adición reciente
Esta es una adición reciente al idioma. Los navegadores antiguos pueden necesitar polyfills.
¿El encadenamiento opcional ?.
es una forma segura de acceder a las propiedades de objetos anidados, incluso si no existe una propiedad intermedia.
Si acaba de comenzar a leer el tutorial y a aprender JavaScript, tal vez el problema aún no lo haya afectado, pero es bastante común.
Como ejemplo, digamos que tenemos objetos user
que contienen información sobre nuestros usuarios.
La mayoría de nuestros usuarios tienen direcciones en la propiedad user.address
, con la calle user.address.street
, pero algunos no las proporcionaron.
En tal caso, cuando intentamos obtener user.address.street
y el usuario no tiene una dirección, obtenemos un error:
dejar usuario = {}; // un usuario sin propiedad "dirección" alerta(usuario.dirección.calle); // ¡Error!
Ese es el resultado esperado. JavaScript funciona así. Como user.address
no está undefined
, un intento de obtener user.address.street
falla con un error.
En muchos casos prácticos preferiríamos quedar undefined
en lugar de un error aquí (que significa "no hay calle").
…y otro ejemplo. En el desarrollo web, podemos obtener un objeto que corresponde a un elemento de la página web usando una llamada a un método especial, como document.querySelector('.elem')
, y devuelve null
cuando no existe tal elemento.
// document.querySelector('.elem') es nulo si no hay ningún elemento let html = document.querySelector('.elem').innerHTML; // error si es nulo
Una vez más, si el elemento no existe, obtendremos un error al acceder a la propiedad .innerHTML
de null
. Y en algunos casos, cuando la ausencia del elemento es normal, nos gustaría evitar el error y simplemente aceptar html = null
como resultado.
¿Cómo podemos hacer esto?
¿La solución obvia sería verificar el valor usando if
o el operador condicional ?
, antes de acceder a su propiedad, así:
dejar usuario = {}; alerta (usuario.dirección? usuario.dirección.calle: indefinido);
Funciona, no hay ningún error… Pero es bastante poco elegante. Como puede ver, la "user.address"
aparece dos veces en el código.
Así es como se vería lo mismo para document.querySelector
:
let html = document.querySelector('.elem')? document.querySelector('.elem').innerHTML: nulo;
Podemos ver que el elemento de búsqueda document.querySelector('.elem')
en realidad se llama dos veces aquí. No es bueno.
Para propiedades más profundamente anidadas, se vuelve aún más feo, ya que se requieren más repeticiones.
Por ejemplo, obtengamos user.address.street.name
de manera similar.
dejar usuario = {}; // el usuario no tiene dirección alerta (usuario.dirección? usuario.dirección.calle? usuario.dirección.calle.nombre: nulo: nulo);
Eso es simplemente horrible, uno puede incluso tener problemas para entender dicho código.
Hay una manera un poco mejor de escribirlo, usando el operador &&
:
dejar usuario = {}; // el usuario no tiene dirección alerta (usuario.dirección && usuario.dirección.calle && usuario.dirección.calle.nombre); // indefinido (sin error)
Hacer AND en toda la ruta a la propiedad garantiza que todos los componentes existan (si no, la evaluación se detiene), pero tampoco es lo ideal.
Como puede ver, los nombres de las propiedades todavía están duplicados en el código. Por ejemplo, en el código anterior, user.address
aparece tres veces.
¿Por eso el encadenamiento opcional ?.
fue agregado al idioma. ¡Para solucionar este problema de una vez por todas!
¿El encadenamiento opcional ?.
detiene la evaluación si el valor anterior ?.
es undefined
o null
y devuelve undefined
.
Más adelante en este artículo, por brevedad, diremos que algo “existe” si no es null
ni undefined
.
En otras palabras, value?.prop
:
funciona como value.prop
, si value
existe,
de lo contrario (cuando value
es undefined/null
) devuelve undefined
.
Esta es la forma segura de acceder a user.address.street
usando ?.
:
dejar usuario = {}; // el usuario no tiene dirección alerta(usuario?.dirección?.calle); // indefinido (sin error)
El código es breve y claro, no hay ninguna duplicación.
Aquí hay un ejemplo con document.querySelector
:
let html = document.querySelector('.elem')?.innerHTML; // no estará definido, si no hay ningún elemento
Leer la dirección con user?.address
funciona incluso si el objeto user
no existe:
dejar usuario = nulo; alerta(usuario?.dirección); // indefinido alerta (usuario?.dirección.calle); // indefinido
Tenga en cuenta: el ?.
La sintaxis hace que el valor anterior sea opcional, pero no más.
Por ejemplo, en user?.address.street.name
el ?.
permite que user
sea null/undefined
de forma segura (y devuelve undefined
en ese caso), pero eso es solo para user
. A otras propiedades se accede de forma regular. Si queremos que algunos de ellos sean opcionales, tendremos que reemplazar más .
con ?.
.
No abuses del encadenamiento opcional
Deberíamos usar ?.
sólo donde está bien que algo no exista.
Por ejemplo, si según nuestra lógica de código el objeto user
debe existir, pero address
es opcional, entonces deberíamos escribir user.address?.street
, pero no user?.address?.street
.
Luego, si user
no está definido, veremos un error de programación al respecto y lo solucionaremos. De lo contrario, ¿si abusamos ?.
, los errores de codificación pueden silenciarse cuando no sea apropiado y volverse más difíciles de depurar.
¿La variable antes ?.
debe ser declarado
Si no hay ningún user
variable, entonces user?.anything
desencadena un error:
// ReferenceError: el usuario no está definido ¿usuario?.dirección;
La variable debe ser declarada (por ejemplo, let/const/var user
o como parámetro de función). El encadenamiento opcional funciona sólo para variables declaradas.
Como se dijo antes, el ?.
detiene inmediatamente (“cortocircuita”) la evaluación si la parte izquierda no existe.
Entonces, si hay más llamadas a funciones u operaciones a la derecha de ?.
, no se harán.
Por ejemplo:
dejar usuario = nulo; sea x = 0; usuario?.sayHi(x++); // no hay "usuario", por lo que la ejecución no llega a la llamada sayHi y x++ alerta(x); // 0, valor no incrementado
¿El encadenamiento opcional ?.
no es un operador, sino una construcción de sintaxis especial, que también funciona con funciones y corchetes.
Por ejemplo, ?.()
se utiliza para llamar a una función que puede no existir.
En el siguiente código, algunos de nuestros usuarios tienen un método admin
y otros no:
dejar usuarioAdmin = { administrador() { alerta("Soy administrador"); } }; dejar usuarioInvitado = {}; usuarioAdmin.admin?.(); //soy administrador usuarioGuest.admin?.(); // no pasa nada (no existe tal método)
Aquí, en ambas líneas primero usamos el punto ( userAdmin.admin
) para obtener la propiedad admin
, porque asumimos que el objeto user
existe, por lo que es seguro leerlo.
Luego ?.()
comprueba la parte izquierda: si la función admin
existe, entonces se ejecuta (es así para userAdmin
). De lo contrario (para userGuest
) la evaluación se detiene sin errores.
La sintaxis ?.[]
también funciona si queremos usar corchetes []
para acceder a las propiedades en lugar de punto .
. Al igual que en casos anteriores, permite leer de forma segura una propiedad de un objeto que puede no existir.
let key = "nombre"; dejar usuario1 = { nombre: "Juan" }; dejar usuario2 = nulo; alerta(usuario1?.[clave]); // John alerta(usuario2?.[clave]); // indefinido
¿También podemos usar ?.
con delete
:
¿eliminar usuario?.nombre; // eliminar nombre.usuario si el usuario existe
¿Podemos utilizar ?.
para leer y borrar de forma segura, pero no para escribir
¿El encadenamiento opcional ?.
no tiene uso en el lado izquierdo de una tarea.
Por ejemplo:
dejar usuario = nulo; usuario?.nombre = "Juan"; //Error, no funciona // porque se evalúa como: indefinido = "John"
¿El encadenamiento opcional ?.
la sintaxis tiene tres formas:
obj?.prop
: devuelve obj.prop
si obj
existe; en caso contrario, undefined
.
obj?.[prop]
– devuelve obj[prop]
si obj
existe; en caso contrario, undefined
.
obj.method?.()
: llama obj.method()
si obj.method
existe; de lo contrario, devuelve undefined
.
Como podemos ver, todos ellos son sencillos y sencillos de utilizar. El ?.
comprueba la parte izquierda en busca de null/undefined
y permite que la evaluación continúe si no es así.
Una cadena de ?.
permite acceder de forma segura a propiedades anidadas.
Aún así, ¿deberíamos aplicar ?.
con cuidado, sólo cuando sea aceptable, según nuestra lógica de código, que la parte izquierda no exista. Para que no nos oculte errores de programación, si se producen.