El problema
Ocurre a menudo que, cuando necesitamos acceder al valor de una propiedad en un objeto Javascript, si ésta no existe, obtenemos un error. Para evitarlo, lo habitual es recorrer la cadena completa, paso a paso, para ir garantizando su consistencia.
Supongamos este objeto:
const user = { fullName: 'Bob Smith', location: { country: 'USA', state: 'Iowa', address: { type: 'building', street: 'SpringFlowers', number: 3 } } }; |
Si intentamos acceder a una propiedad anidada a través de un ancestro que no existe, el intérprete nos devuelve el error:
user.phone.mobile; // Uncaught TypeError: Cannot read property 'mobile' of undefined |
Para alcanzar de forma segura a una propiedad, debemos recorrer toda su cadena ancestral comprobando en cada paso que éstos existen. Por lo general, aplicamos para ello una evaluación perezosa de tipo cortocircuito:
user && user.location.address.street; // SpringFlowers |
Como se puede ver, esta forma de acceso es poco práctica: a mayor nivel de profundidad, mayor cadena de evaluación.
Objetivo
Para solucionar este escenario, vamos a crear una función o método que haga el trabajo sucio por nosotros, dejándonos en nuestro códigos una evaluación más simple y legible.
Cuándo puede ser útil
- Tenemos objetos creados dinámicamente y queremos obtener el valor de una propiedad anidada que, puede o no, existir.
- Queremos mejorar la legibilidad de nuestro código fuente utilizando una función independiente para acceder a nuestras propiedades.
Soporte y dependencias
- No es necesario el uso de ninguna dependencia.
El código.
Versión ES5
/** * Get a nested object property value * Checks for a nested object property and returns its value. * @param {object} obj An object given * @param {string} key The needed key * @return {string} The value of that key. */ var getProp = function (obj, key) { return key.split('.').reduce( function (o, x) { return (typeof o == "undefined" || o === null) ? o : o[x]; }, obj); }; |
Versión ES6
const getProp = (obj, key) => key.split('.').reduce( (o, x) => (typeof o == "undefined" || o === null) ? o : o[x] , obj); |
Funcionamiento
Nuestra función toma dos parámetros: el objeto sobre el que queremos actuar, y una cadena que marca el camino de la propiedad requerida.
La idea base es dividir esa cadena de propiedades en un array para ir comprobando, de forma iterativa, si ésta existe en nuestro objeto.
Ejemplo de uso, el código completo
Basándonos en los ejemplos anteriores, presentamos aquí todo el código completo interactivo:
// The function const getProp = (obj, key) => key.split('.').reduce( (o, x) => (typeof o == "undefined" || o === null) ? o : o[x] , obj); // Our mock object const user = { fullName: 'Bob Smith', location: { country: 'USA', state: 'Iowa', address: { type: 'building', street: 'SpringFlowers', number: 3 } } }; // Implementation getProp(user, 'phone.mobile'); //undefined getProp(user, 'location.address.street'); //SpringFlowers
Muuuy útil, cómo siempre.
Muuuchas gracias.
Hace unas semanas fb sacó idx, que me gusta mucho https://facebook.github.io/react-native/blog/2017/03/13/idx-the-existential-function.html
Gracias por el enlace!
Efectivamente, esta herramienta ataca el mismo problema proponiendo una sintaxis alternativa (¿y algo confusa?).
Ya es cuestión de cada desarrollador adoptar la forma que más cómoda y elegante le resulte.
Saludos!
Muy buen tip, solo tengo una consulta, porque la sentencia typeof o == «undefined» debe ir con doble igualdad? yo entiendo la diferencia entre la doble igualdad y la triple, pero para este caso en particular me deja la duda del porque usar doble para undefined y tripe para null.
Este blog lo visito regularmente para evacuar dudas, gran trabajo!
Saludos