Encadenamiento Alternativo ‘?.’
Una reciente adición
Esta es una mejora reciente en el lenguaje. Los navegadores antiguos podrían necesitar adaptaciones.
El encadenamiento opcional ‘?.’ proporciona una manera segura de acceder a propiedades anidadas de objetos, incluso cuando una propiedad intermedia podría no existir.
El problema de las propiedades inexistentes
Si estás empezando a aprender JavaScript, es posible que aún no hayas enfrentado este problema, pero es bastante común.
Por ejemplo, supongamos que tenemos objetos de usuario que contienen información de nuestros usuarios. La mayoría tiene la dirección almacenada en user.address, con la calle en user.address.street, pero algunos no tienen esta información.
En tal caso, al intentar acceder a user.address.street en un usuario sin dirección, obtendríamos un error:
let user = {}; // usuario sin propiedad "address"
alert(user.address.street); // ¡Error!
JavaScript genera un error porque user.address es undefined, y al intentar acceder a user.address.street, falla.
En muchos casos, preferiríamos obtener undefined en lugar de un error, indicando que la calle no está definida.
Otro ejemplo común es en desarrollo web, donde obtenemos un objeto correspondiente a un elemento de la página usando document.querySelector(‘.elem’), que devuelve null si no existe el elemento.
// Error si el resultado de querySelector(...) es null
let html = document.querySelector('.my-element').innerHTML;
Nuevamente, si el elemento no existe, al intentar acceder a .innerHTML de null obtendremos un error. En algunos casos, queremos evitar este error y simplemente aceptar html = null como resultado.
¿Cómo podemos manejar esto?
La solución obvia sería verificar el valor usando if o el operador condicional ? antes de usar la propiedad:
let user = {};
alert(user.address ? user.address.street : undefined);
Esto funciona, pero no es elegante. Como se puede ver, «user.address» aparece dos veces en el código.
Lo mismo ocurre con document.querySelector:
let html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null;
Aquí, document.querySelector(‘.elem’) se llama dos veces. No es ideal.
Cuando las propiedades están anidadas profundamente, esto se convierte en un problema mayor debido a la repetición.
Por ejemplo, intentar obtener user.address.street.name de manera similar se vuelve complicado:
let user = {}; // usuario sin dirección
alert(user.address ? user.address.street ? user.address.street.name : null : null);
Esto es difícil de leer y mantener.
Encadenamiento opcional
El encadenamiento opcional ?. detiene la evaluación y devuelve undefined si el valor antes del ?. es undefined o null.
Aquí está cómo se ve el acceso seguro a user.address.street usando ?:
let user = {}; // usuario sin dirección
alert( user?.address?.street ); // undefined (sin error)
El código es conciso y claro, sin duplicación de código.
Aquí hay un ejemplo con document.querySelector:
let html = document.querySelector('.elem')?.innerHTML; // será undefined si no existe el elemento
Incluso leer la dirección con user?.Address funciona aunque el objeto user no exista:
let user = null;
alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
Nota: la sintaxis ?. hace opcional el valor delante de ella, pero no más allá.
Por ejemplo, en user?.address.street.name, el ?. hace que user pueda ser null/undefined (devolviendo undefined en ese caso), pero solo a user. Las propiedades restantes se acceden de manera normal. Si queremos que algunas de estas también sean opcionales, necesitamos usar más ?.
Uso adecuado del encadenamiento opcional
Deberíamos usar ?. solo donde sea aceptable que algo no exista.
Por ejemplo, si según la lógica de nuestro código, el objeto user debe existir pero address es opcional, deberíamos escribir user.address?.street y no user?.address?.street.
Esto asegura que si por error user no está definido, lo sabremos y podremos corregirlo. De lo contrario, los errores de programación pueden silenciarse incorrectamente, dificultando la depuración.
La variable antes de ?. debe estar declarada
Si la variable user no está declarada con let/const/var o como parámetro de función, user?.anything generará un error:
// ReferenceError: user no está definido
user?.address;
El encadenamiento opcional solo funciona para variables declaradas.
Cortocircuitos
Como se mencionó antes, ?. detiene inmediatamente («cortocircuita») la evaluación si la parte izquierda no existe.
Entonces, si a la derecha de ?. hay funciones u operaciones adicionales, estas no se ejecutarán:
let user = null;
let x = 0;
user?.sayHi(x++); // "user" no está definido, por lo que la ejecución no alcanza sayHi ni x++
alert(x); // 0, el valor no se incrementa
Otros casos: ?.(), ?.[]
El encadenamiento opcional ?. no es solo para propiedades, también funciona con funciones y corchetes.
Por ejemplo, ?.() se usa para llamar a una función que podría no existir.
En el siguiente código, algunos usuarios tienen el método admin y otros no:
let userAdmin = {
admin() {
alert("Soy administrador");
}
};
let userGuest = {};
userAdmin.admin?.(); // "Soy administrador"
userGuest.admin?.(); // no hace nada (el método no existe)
En ambas líneas, primero usamos el punto (userAdmin.admin) para obtener la propiedad admin, asumiendo que el objeto user existe y es seguro leerlo. Luego, ?.() verifica si la función admin existe antes de ejecutarla.
La sintaxis ?.[] también funciona si preferimos usar corchetes [] para acceder a propiedades en lugar de punto ..
let key = "firstName";
let user1 = {
firstName: "John"
};
let user2 = null;
alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined
Podemos usar ?. con delete:
delete user?.name; // Elimina user.name si user existe
No podemos usar ?. para asignar valores:
let user = null;
user?.name = "John"; // Error, no funciona
// porque se evaluaría como: undefined = "John"
Resumen
La sintaxis del encadenamiento opcional ?. tiene tres formas:
- obj?.prop: devuelve obj.prop si obj existe, de lo contrario, undefined.
- obj?.[prop]: devuelve obj[prop] si obj existe, de lo contrario, undefined.
- obj.method?.(): llama a obj.method() si obj.method existe, de lo contrario, devuelve undefined.
Estas formas son simples y fáciles de usar. El ?. verifica si la parte izquierda es null/undefined y permite que la evaluación continúe si es así.
Una cadena de ?. permite el acceso seguro a propiedades anidadas.
Aún así, debemos aplicar ?. con cuidado, solo donde sea aceptable que la parte izquierda no exista según la lógica de nuestro código. Esto es para evitar ocultar errores de programación que necesitan corrección.