Introducción
La desestructuración es otra de las novedades potentes del nuevo estándar ES6 (ECMAScript 2015). Si bien, por su definición, no es una funcionalidad que pueda parecer compleja, sí lo es cuando comenzamos a profundizar en sus posibilidades. La razón de esto es la habitual en Javascript: el lenguaje, debido a su propia estructura, puede ser extremadamente flexible y, gracias a esa versatilidad, toda nueva característica puede ser explotada hasta sus límites teóricos.
Veamos en esta primera parte cómo funciona la desestructuración en su modo más básico con varios ejemplos antes de meternos en construcciones de mayor envergadura.
¡Vamos a ello!
Definición y ejemplo básico
La desestructuración no es un concepto nuevo en programación. De hecho, eso es algo que se ha tenido muy en cuenta a la hora de fijar el estándar: incorporar de forma progresiva al lenguaje Javascript lo mejor de otros. En Python o en OCaml, tendríamos las tuplas (aquí y aquí respectivamente); las listas en PHP; o sus corresponientes en Perl y Clojure…
En este caso, la desestructuración podemos definirla como una expresión que permite asignar valores a nombres conforme a una estructura de tabla dada. Veamos un ejemplo rápido y simple:
[ myVarOne, myVarTwo ] = [ 'foo', 'bar' ]; console.info( myVarOne ); // foo console.info( myVarTwo ); // bar |
NOTA: Los corchetes a cada lado de la igualdad nos sirven para marcar cada uno de los conjuntos (variables y valores). Más adelante veremos variaciones…
La idea se ve fácilmente: usando una única instrucción, tomamos un conjunto de variables (bloque de la izquierda) y le asignamos un conjunto de valores (bloque de la derecha).
Lo que hace de esta estructura algo que da mucho juego es cómo formemos los bloques a cada lado de la igualdad, algo que iremos viendo poco a poco.
Declaración de variables
Como ocurre con cualquier variable en Javascript, ésta ha de ser declarada para evitar contaminar el espacio global. Durante la desestructuración, la declaración puede realizarse tanto en la misma instrucción como con anterioridad. También es posible utilizar cualquiera de las formas actuales (visibilidad) con que contamos para declarar variables en el lenguaje:
var a, b; let c, d; [ a, b, c, d ] = [ 'La', 'donna', 'e', 'mobile' ]; var [ e, f ] = [ 'cual', 'piuma' ]; let [ g, h ] = [ 'al', 'vento' ]; console.info( a, b, c, d, e, f, g, h ); // La donna e mobile cual piuma al vento |
NOTA: Para saber más sobre la declaración con LET, recomiendo el artículo donde analicé esta instrucción en profundidad.
Con las constantes, sin embargo, no pueden definirse previamente para asignar su valor después:
const ONE; // SyntaxError [ ONE ] = [ 'foo' ]; |
Eso ocurre porque las constantes deben iniciarse con un valor. Sí funciona si realizamos la declaración directamente junto a la desestructuración:
const [ ONE, TWO ] = [ 'foo', 'bar' ]; console.info( ONE, TWO ); // foo bar |
NOTA: Para saber más sobre las constantes, recomiendo leer el artículo donde las analicé en profundidad.
Desestructuración con objetos
Los ejemplos anteriores se han realizado sobre arrays, pero también es posible utilizar objetos. En ese caso, los corchetes se reemplazan por llaves como en la notación literal.
Ejemplos con objetos:
var obj = { foo: 'Hello', bar: 'World' }; var { foo, bar } = obj; console.info( foo, bar ); // Hello World |
Si el objeto no contiene las claves que solicitamos, se les asocia automáticamente el valor ‘undefined‘:
var { a, b } = obj; console.info( a, b ); // undefined undefined |
Si nuestras variables ya han sido declaradas, no podremos utilizar solo llaves para indicar el primer conjunto:
var obj = { foo: 'Hello', bar: 'World' }; var foo, bar; { foo, bar } = obj; //SyntaxError |
Esto es así porque las llaves están indicando un bloque/contexto (lo que sería una estructura perfectamente válida en Javascript). Para que funcione, necesitamos enmarcar toda la instrucción entre paréntesis:
( { foo, bar } = obj ); console.info( foo, bar ); // Hello World |
Número de elementos en los conjuntos
No es necesario que ambos conjuntos tengan el mismo número de elementos. Cuando son diferentes, el comportamiento se corresponde con lo siguiente:
- Si un elemento del conjunto de la izquierda no encuentra correspondencia en el de la derecha, recibirá un valor ‘undefined‘.
- Si hay más elementos en el conjunto de la derecha que en de la izquierda, estos se desprecian.
Ejemplo caso 1:
var [ a, b, c ] = [ 'Hello', 'World' ]; console.info( a, b, c ); // Hello World undefined |
Ejemplo caso 2:
var [ a, b ] = [ 'La', 'donna', 'e', 'mobile' ]; console.info( a, b ); // La donna |
Ignorando valores
También podemos utilizar la elisión para ignorar valores tanto en uno como en otro conjunto:
var [ a, , b, , c ] = [ 'la', 'donna', 'e', 'mobile', 'cual', 'piuma' ]; console.info( a, b, c ); // la e cual |
NOTA: la elisión (dos comas consecutivas), se interpreta como un hueco e ignora su valor correspondiente en el otro conjunto. ¡Incluso podemos ignorar todo el conjunto!:
var [ , , , , ] = [ 'la', 'donna', 'e', 'mobile', 'cual', 'piuma' ]; |
Lo anterior funciona de modo similar cuando la elisión se realiza en el conjunto de los valores:
var [ a, b, c, d ] = [ 'la', , 'donna', , 'e', 'mobile' ]; console.info( a, b, c, d ); // la undefined donna undefiend |
Fuente de los conjuntos
Aquí empezamos un poco con la fiesta. En los ejemplos anteriores, los conjuntos estaban definidos de forma explícita a cada lado de la igualdad. Pero como cabría esperar, también podemos utilizar cualquier construcción Javascript válida que devuelva valores. El ejemplo más directo de esto, serían las funciones:
var foo = () => [ 'La', 'donna', 'e', 'mobile' ]; var [ a, b, c, d ] = foo(); console.info( a, b, c, d ); // La donna e mobile |
NOTA: En este caso, dado que nuestra función devuelve un array (una tabla) no necesitamos los corchetes para limitar el conjunto.
En el ejemplo anterior, nuestra función devolvía los valores. Sin embargo, parece que no es posible utilizar una función como fuente para las asignaciones:
var a, b, c, d; var foo = () => [ a, b, c, d ]; var bar = () => [ 'la', 'donna', 'e', 'mobile' ]; foo() = [ 'La', 'donna', 'e', 'mobile' ]; // Invalid assignment [ foo() ] = [ 'La', 'donna', 'e', 'mobile' ]; // Invalid destructuring target [ foo() ] = [ bar() ]; // Invalid destructuring target |
Procesado con funciones
Si bien las funciones pueden devolver los valores que asignamos a las variables, el uso más interesante de esta posibilidad es la de procesarlos acorde a las necesidades del programa.
Veamos un ejemplo donde una función nos sirve para alterar los valores de entrada, en este caso, convirtiendo a mayúsculas una cadena dada :
var upper = ( ...args ) => args.map( x => x.toUpperCase() ); var [ a, b, c, d ] = upper( 'la', 'donna', 'e', 'mobile' ); console.info( a ); // LA console.info( b ); // DONNA console.info( c ); // E console.info( d ); // MOBILE |
La función ‘upper’ toma todos los argumentos que se le pasen vía el operador de propagación o arrastre y los pasa a mayúsculas. Se trataría de una Función Pura que podemos utilizar para asignar valores en batería gracias a la desestructuración.
Conclusión (de esta primera parte)
Gracias a los ejemplos anteriores podemos hacernos una idea de la flexibilidad y potencia de la desestructuración en Javascript: se trata de un método rápido para asignar valores en bloque a una tabla dada a partir de una segunda colección de valores. Esta funcionalidad puede aplicarse tanto a arrays como a objetos, con igual o diferente número de elementos en cada uno de sus términos. Los valores asignados permiten ser procesados y manipulados en pasos intermedios, pueden ser omitidos o filtrados y, en general, funcionan con cualquier construcción del lenguaje que devuelva valores.
El mayor juego vendrá cuando comprobemos cómo combinar esta funcionalidad con otras de las nuevas características del lenguaje para conseguir unos resultados más elegantes y legibles que los que un código más tradicional nos permiten.
En la siguiente parte de esta serie, revisaremos varios ejemplos prácticos, con algún truco interesante, ciertas limitaciones que podemos encontrarnos y errores frecuentes.
Aún no tenemos debug!
Ningún marciano se ha comunicado.