JS: Objetos

Teoría: Objetos anidados

Cuando trabajamos con objetos en JavaScript, una propiedad puede ser cualquier cosa: un número, una cadena, un booleano... ¡o incluso otro objeto o un array! Esto nos permite modelar datos de manera estructurada y organizada. Veamos cómo funciona.

Crear objetos con propiedades anidadas

Imagina que tenemos un usuario con información como su nombre, edad, estado civil, una lista de amigos, sus hijos y la empresa donde trabaja. Esto lo podemos representar mediante un objeto en JavaScript:

const user = { name: 'Miguel', married: true, age: 25 };

// Agregar la propiedad friends con una lista de amigos
user.friends = ['Alejandro', 'Samuel'];

// Agregar la propiedad children con una lista de hijos (cada hijo es un objeto)
user.children = [
  { name: 'María', age: 1 },
  { name: 'Sebastián', age: 10 },
];

// Agregar un objeto anidado para la empresa
user.company = { name: 'Códica' };

console.log(user);

El resultado al imprimir user será:

{
  name: 'Miguel',
  married: true,
  age: 25,
  friends: ['Alejandro', 'Samuel'],
  children: [
    { name: 'María', age: 1 },
    { name: 'Sebastián', age: 10 }
  ],
  company: { name: 'Códica' }
}

También podemos definir toda esta estructura directamente al crear el objeto:

const user = {
  name: 'Miguel',
  married: true,
  age: 25,
  friends: ['Alejandro', 'Samuel'],
  children: [
    { name: 'María', age: 1 },
    { name: 'Sebastián', age: 10 },
  ],
  company: { name: 'Códica' },
};

Acceder a propiedades anidadas

Cuando trabajamos con objetos anidados o arrays dentro de un objeto, accedemos a los valores utilizando puntos (.) o corchetes ([]):

user.friends[1];       // 'Samuel'
user.children[0].name; // 'María'
user.company.name;     // 'Códica'

// También se puede hacer con corchetes
user['children'][0]['name']; // 'María'

Imprimir Objetos Complejos

Cuando usamos console.log() en JavaScript, hay una limitación: si un objeto tiene propiedades anidadas muy profundas, en la consola solo veremos [Object] o [Array] en lugar del contenido real:

const obj = { a: { b: { c: { key: 'value' }, e: [1, 2] } } };
console.log(obj);
// { a: { b: { c: [Object], e: [Array] } } }

Para evitar esto, podemos usar JSON.stringify() para convertir el objeto en una cadena de texto y visualizarlo correctamente:

console.log(JSON.stringify(obj));
// {"a":{"b":{"c":{"key":"value"},"e":[1,2]}}}

// También podemos usar un formato con indentación
console.log(JSON.stringify(obj, null, 2));
/* 
{
  "a": {
    "b": {
      "c": {
        "key": "value"
      },
      "e": [1, 2]
    }
  }
}
*/

Verificando Propiedades Anidadas

A veces, necesitamos comprobar si una propiedad dentro de un objeto anidado existe antes de acceder a ella. Sin protección, intentar acceder a una propiedad que no existe puede generar un error:

// Intentando acceder a una propiedad que no existe
console.log(user.company.address.street); // ❌ Error: Cannot read properties of undefined

Una solución tradicional sería usar una serie de verificaciones:

if (user.company && user.company.address && user.company.address.street) {
  console.log(user.company.address.street);
}

Pero esto es poco práctico. Afortunadamente, JavaScript ofrece maneras más eficientes.

Operador de Encadenamiento Opcional (?.)

El operador de encadenamiento opcional (?.) simplifica el acceso a propiedades anidadas sin necesidad de verificar cada nivel:

console.log(user.company?.address?.street); // undefined

Gracias a este operador, si alguna propiedad en la cadena no existe, simplemente devuelve undefined en lugar de generar un error.

También funciona con claves dinámicas:

const key = 'address';
console.log(user.company?.[key]?.street); // undefined

Operador de Fusión Nula (??)

El operador de fusión nula (??) permite establecer un valor por defecto cuando la propiedad no existe o es null o undefined:

console.log(user.company?.address?.street ?? 'No disponible'); // 'No disponible'

Diferencia con el operador || (o lógico):

const value = false;

console.log(value ?? 'Valor por defecto'); // false
console.log(value || 'Valor por defecto'); // 'Valor por defecto'

?? solo reemplaza valores null o undefined, mientras que || también considera false, 0 o "" (cadena vacía) como valores "falsos".

Usando get() de Lodash

Si estamos trabajando con datos muy anidados y claves dinámicas, una alternativa más limpia es la función _.get() de la biblioteca Lodash:

import _ from 'lodash';

const obj = {};

const value = _.get(obj, 'one.two.three', 'Valor por defecto'); // 'Valor por defecto'

También acepta un array de claves:

_.get(obj, ['one', 'two', 'three'], 'Valor por defecto'); // 'Valor por defecto'

Resumen

  • En un objeto, las propiedades pueden contener otros objetos o arrays.
  • Se accede a propiedades anidadas con obj.prop.subprop o obj['prop']['subprop'].
  • console.log(obj) tiene limitaciones con estructuras anidadas; JSON.stringify(obj, null, 2) ayuda a visualizar mejor el contenido.
  • Para evitar errores al acceder a propiedades anidadas, se pueden usar:
    • El operador de encadenamiento opcional (?.).
    • El operador de fusión nula (??).
    • La función _.get() de Lodash.

Completado

0 / 13