/ javascript

Destructuring en Javascript

"Todo es muy difícil antes de ser sencillo" Thomas Fuller

Hay una nueva programación que viene de camino. Una programación que da prioridad a una lectura fácil sacrificando un poco de dificultad a la hora de escribir el código. Ya hemos hablado de ello en posts como La energía del código y el destructuring de Javascript es una de las nuevas características de ES6 que sigue esa tendencia. Recuerda mucho al Pattern matching de Haskell; pero programación funcional o no, lo que está claro es que esta nueva opción del lenguaje nos permite escribir código mucho más limpio y claro, y es nuestra obligación hacer uso del destructuring siempre que nos permita dejar un código con más energía.

Definición

Destructuring, o destructuración en un lenguaje más hispanizado, es una nueva característica de ES6 para Javascript que nos da la posibilidad de poder coger los datos de objetos o arrays directamente y de manera múltiple, para extraerlos a variables o constantes.

Sintaxis

La sintaxis del destructuring es muy sencilla. Por un lado tenemos el objeto que queremos destructurar. Para extraer sus propiedades usamos las { }, metiendo dentro de ellas sus respectivos nombres y con esto tenemos nuevas variables que contienen estas propiedades:


const person = {
    name: 'Pepe',
    age: 26,
    hobbies: ['chess', 'running', 'basket']
}

const { name, age, hobbies } = person;

console.log(name); // result => Pepe

Si queremos poner nombres específicos para estas nuevas variables bastará con poner : seguido del nuevo nombre de variable que queramos asignar en las propiedades destructurada:


const person = {
    name: 'Pepe',
    age: 26,
    hobbies: ['chess', 'running', 'basket']
}

const { name: personName, age: personAge, hobbies } = person;

console.log(name); // result => undefined
console.log(personName); // result => Pepe


Y de igual manera podemos destructurar los arrays:


const person = {
    name: 'Pepe',
    age: 26,
    hobbies: ['chess', 'running', 'basket']
}

const { name: personName, age: personAge, hobbies } = person;

const [ firstHobbie, secondHobbie, thirdHobbie ] = hobbies;

console.log(secondHobbie); // result => running

Uso Práctico

Después de haber visto la sintaxis del destructuring seguro que os queda la duda de: ¿dónde podría aplicar yo esto para que mi código fuese más limpio y legible? La destructuración se puede hacer en muchos sitios, pero los más beneficiosos suelen ser:

  • Retornos de funciones
  • Parámetros en las funciones
  • Funciones de trabajo con arrays
  • Destructuring múltiple
  • Importación de objetos
  • Destructuring en React

Retornos de funciones.

Cuando recuperamos datos de una función solemos recuperar un objeto con propiedades que nos devuelve el resultado de la misma. Para tener que interaccionar con sus propiedades tenemos que ir hasta ellas desde el objeto. Algo que podemos evitar si usamos adecuadamente el destructuring.


function getPerson() {
    return {
        name: 'Pepe',
        age: 26,
        hobbies: ['chess', 'running', 'basket']
    }
}

const { name } = getPerson();

console.log(name); // result => Pepe.

Como se puede ver podemos cazar al vuelo las propiedades devueltas por una función. Esto hace que el código sea mucho más limpio y que podamos atacar a las propiedades que de verdad nos interesan de los objetos que nos devuelven las funciones. Como podéis observar no he destructurado ni age ni hobbies ya que son propiedades con las que no voy a trabajar.

Parámetros en las funciones

Muchas veces no usamos todos los parámetros de nuestras funciones y solo necesitamos un par de propiedades. En estos casos destructuring también nos puede ayudar a dejar un código más claro. Pongamos una función filter para poder filtrar las personas que tenemos en un array. Esta función filter recibe un objeto con las opciones de filtrado de nombre y edad.


const people = [
    {
        name: 'Pepe',
        age: 26,
        hobbies: ['chess', 'running', 'basket']
    },
    {
        name: 'Juan',
        age: 32,
        hobbies: [ 'basket' ]
    },
    {
        name: 'Paco',
        age: 45,
        hobbies: ['running']
    }
]

function getPeople(filter) {
    return people.filter(person => (!filter.nameFilter || person.name === filter.nameFilter) && 
                                   (!filter.minAge || person.age > filter.minAge));
}

const peopleBiggerThan40 = getPeople({ minAge: 40 });

console.log(peopleBiggerThan40); 
// result => [
//    {
//        name: 'Paco',
//        age: 45,
//        hobbies: ['running']
//    }
// ]

Esta función queda más limpia aplicando el destructuring en los parámetros de la misma:


const people = [
    {
        name: 'Pepe',
        age: 26,
        hobbies: ['chess', 'running', 'basket']
    },
    {
        name: 'Juan',
        age: 32,
        hobbies: [ 'basket' ]
    },
    {
        name: 'Paco',
        age: 45,
        hobbies: ['running']
    }
]

function getPeople({ nameFilter, minAge }) {
    return people.filter(person => (!nameFilter || person.name === nameFilter) && 
                                   (!minAge || person.age > minAge));
}

const peopleBiggerThan40 = getPeople({ minAge: 40 });

console.log(peopleBiggerThan40); 
// result => [
//    {
//        name: 'Paco',
//        age: 45,
//        hobbies: ['running']
//    }
// ]

También podemos añadir a los parámetros destructurados valores por defecto si nos interesase:


const people = [
    {
        name: 'Pepe',
        age: 26,
        hobbies: ['chess', 'running', 'basket']
    },
    {
        name: 'Juan',
        age: 32,
        hobbies: [ 'basket' ]
    },
    {
        name: 'Paco',
        age: 45,
        hobbies: ['running']
    }
]

function getPeople({ nameFilter, minAge = 30 }) {
    return people.filter(person => (!nameFilter || person.name === nameFilter) && 
                                   (!personAge || person.age > minAge));
}

const peopleBiggerThan40 = getPeople({});

console.log(peopleBiggerThan40); 
// result => [
//    {
//        name: 'Juan',
//        age: 32,
//        hobbies: [ 'basket' ]
//    },
//    {
//        name: 'Paco',
//        age: 45,
//        hobbies: ['running']
//    }
// ]

Funciones de trabajo con arrays

También es interesante usar destructuring en las funciones para trabajar con arrays. Podéis verlas en nuestro post de arrays. En estas funciones la combinación de arrows functions con el destructuring suele generar un código muy legible:

Por ejemplo la función anterior podría quedar reducida a:


const people = [
    {
        name: 'Pepe',
        age: 26,
        hobbies: ['chess', 'running', 'basket']
    },
    {
        name: 'Juan',
        age: 32,
        hobbies: [ 'basket' ]
    },
    {
        name: 'Paco',
        age: 45,
        hobbies: ['running']
    }
]

function getPeople({ nameFilter, minAge }) {
    return people.filter(({ name, age }) => (!nameFilter || name === nameFilter) && 
                                            (!minAge || age > minAge));
}

const peopleBiggerThan40 = getPeople({ minAge: 40 });

console.log(peopleBiggerThan40); 
// result => [
//    {
//        name: 'Paco',
//        age: 45,
//        hobbies: ['running']
//    }
// ]

Hemos destructurado el objeto person dentro de la arrow function del filter. Así todo queda mucho más conciso y claro y no hace falta usar person. para poder acceder a las propiedades de person.

Destructuring múltiple

Podemos llevar el destructuring de objetos hasta su máxima expresión, es decir, podemos hacer destructuring dentro de nuestro propio destructuring hecho.


const people = [
    {
        names: {
            name: 'Pepe',
            surname: 'Gonzalez'
        },
        age: 26,
        hobbies: ['chess', 'running', 'basket']
    },
    {
        names: {
            name: 'Pepe',
            surname: 'Gonzalez'
        },
        age: 32,
        hobbies: [ 'basket' ]
    },
    {
        names: {
            name: 'Pepe',
            surname: 'Gonzalez'
        },
        age: 45,
        hobbies: ['running']
    }
]

function getPeople({ nameFilter, minAge }) {
    return people.filter({ names: { name }, age } => (!nameFilter || name === nameFilter) && (!minAge || personAge > minAge));
}

const peopleBiggerThan40 = getPeople({ age: 40 });

console.log(peopleBiggerThan40); 
// result => [
//    {
//        name: 'Paco',
//        age: 45,
//        hobbies: ['running']
//    }
// ]

Esta vez el parámetro name ha sido cogido por una doble destructuración. Podemos tener todas las destructuring que tengamos siempre que no lo mezclemos con el destructuring de arrays, ya que no serían compatibles uno con otro.

En cambio, si realizamos el destructuring solo de arrays también lo podemos realizar de manera múltiple:


const [first, [[second], third]] = ['apple', [['banana'], 'orange']];

console.log(first);
// apple
console.log(second);
// banana
console.log(third);
// orange

Este array de arrays ha sido destructurado. Si lo analizamos el elemento 'apple' ha sido destructurado de una manera simple. En cambio 'banana' está dentro de un array que a la vez está dentro de un array y hemos podido hacer el destructuring múltiple sin ningún problema. 'orange' finalmente es un elemento dentro de un array por lo que su nivel de destructuración ha sido de 2.

Importación de módulos de ES6

Otro uso muy interesante del destructuring es en la importación de módulos de es6. En lugar de importarnos el objeto entero podemos destructurarlo:

Sin destructuring:


import React from 'react';

export class App extends React.Component {
	render() {
		return (
			<div>hello world!</div>
		);
	}
}

Con destructuring:


import React, { Component } from 'react';

export class App extends Component {
	render() {
		return (
			<div>hello world!</div>
		);
	}
}

Destructuring en React

Destructuring es una técnica usada mucho en los componentes React ya que les da mucha más legibilidad. Las props suelen ser un blanco fácil para poder realizar el destructuring siempre que se use más de una.

Si comparados dos códigos con o sin destructuring podemos ver la diferencia entre ellos.

Sin destructuring:


import React from 'react';

import { SearchPanel } from './MasterPage/SearchPanel.js';
import { SearchResult } from './MasterPage/SearchResults.js';

import { root } from './masterPage.scss';

const MasterPage = (props) => (
	<main className={root}>
		<SearchPanel search={props.search} />
		<SearchResult results={props.results} isLoading={props.isLoading} />
	</main>
);

export { MasterPage };

Con destructuring:


import React from 'react';

import { SearchPanel } from './MasterPage/SearchPanel.js';
import { SearchResult } from './MasterPage/SearchResults.js';

import { root } from './masterPage.scss';

const MasterPage = ({ search, results, isLoading }) => (
	<main className={root}>
		<SearchPanel search={search} />
		<SearchResult results={results} isLoading={isLoading} />
	</main>
);

export { MasterPage };

Conclusiones

Destructuring es una nueva característica muy potente del lenguaje que nos permite escribir código más legible, aunque como todo no hay que abusar de ella. Esto han sido pequeños ejemplos, pero seguramente si lo que vas a destructurar solamente tiene una propiedad, la mejor opción no sea usarlo. Destructuring coge mucha fuerza sobre todo cuando se usan varias propiedades de un mismo objeto que se pueden destructurar, ya que al hacer el desglose de las propiedades todo suele quedar más limpio y claro.