¡Esta es una revisión vieja del documento!
Tabla de Contenidos
Desestructuración
Módulo perteneciente al curso Programación avanzada con JavaScript y ECMAScript.
Interpolación de cadenas
La interpolación de cadenas es una pequeña novedad de ECMAScript 2015, que a pesar de ser syntactic sugar puro, seguro que se convertirá en una de tus novedades favoritas.
Actualmente en JavaScript podemos crear cadenas de dos formas, ya sea con comillas dobles (") o con las comillas simples ('). Por si alguna vez te lo has preguntado, no hay diferencia alguna entre usar comillas dobles o simples. La razón por la que existan esas dos opciones de caracteres para delimitar las cadenas es porque de ese modo evitamos tener que “escaparlos” si queremos utilizarlos dentro de una cadena:
var str = "Sayonara 'baby'"; var str2 = 'Sayonara \'baby\''; var str3 = 'Hello "amigo"'; var str4 = "Hello \"amigo\"";
Como se puede observar las versiones que no requieren escapar ningún carácter, son más legibles. Esa es la razón principal por la cual existen dos delimitadores distintos para una cadena: elige el que prefieras y usa el otro dentro.
Si alguna vez construyes JSON de forma manual (aunque no deberías hacerlo nunca, sino que deberías usar JSON.stringify en su lugar) recuerda que las cadenas en JSON siempre son con comillas dobles.
Interpolando cadenas
Bajo el nombre de “interpolación” de cadenas no se esconde nada más que un mecanismo que te permite pasar una cadena que contiene un conjunto de comodines o placeholders junto con unos datos. El resultado es la misma cadena pero sustituyendo cada uno de los comodines por el dato concreto.
Veamos cómo podríamos hacer eso en ECMAScript 5:
String.prototype.format = function() { var args = arguments; return this.replace(/{(\d+)}/g, function(match, number) { return typeof args[number] != 'undefined' ? args[number] : match ; }); };
Este código añade al String.prototype (prototipo de todas las cadenas) la función format. Esta función usa una expresión regular para sustituir los placeholders en forma {0}, {1} y así sucesivamente, con el argumento correspondiente al orden indicado:
var str = "{0}{1} mola mucho, pero {0} {2} todavía mola más".format("ECMASCript","5","2015");
El valor de str es: "ECMASCript5 mola mucho, pero ECMASCript 2015 todavía mola más".
Tener un método para interpolar cadenas nos evita tener que usar concatenaciones que al final terminan ensuciando mucho el código.
Pues bien en ECMAScript 2015 las tenemos de serie, en el lenguaje. Sin necesidad de invocar a función alguna y ¡encima con más potencia! Observa el siguiente código:
var a0 = "ECMASCript"; var a1 = 5; var a2 = 2015; var str=`${a0} ${a1} mola mucho, pero ${a0} ${a2} todavía mola más`;
Este código deja en str el mismo valor que antes.
Fijémonos en los detalles:
- Se usa un nuevo delimitador de cadenas: la tilde abierta (
`). La interpolación de cadenas solo funciona con ese delimitador. - Se usa
${expr}para indicar un comodín. La potencia viene dada porque realmente epxr no tiene que ser simplemente una variable (como en nuestro ejemplo) sino que es una expresión. Esa expresión se evaluará y su resultado se colocará en lugar del placeholder.
Veamos otro código que deje claro la evaluación de expresiones dentro de los placeholders:
var a = 10; var b = 32; var str = `El resultado de sumar ${a} y ${b} es ${a+b}`;
En resumen, la interpolación de cadenas nos permite crear fácilmente cadenas compuestas por varios elementos de forma sencilla y mucho más legible que usando concatenaciones. Ya verás que cuando empieces a usarla… ¡te costará volver a concatenar cadenas!
Desestructuración en arrays
La desestructuración (en inglés destructuring) es una nueva característica en ECMAScript 2015 que permite extraer fácilmente elementos de arrays y objetos y asignarlos a variables.
La desestructuración está basada en el pattern matching, una característica típica de los lenguajes funcionales.
Veamos un ejemplo de su uso:
var [a,b] = [1,2];
Este código deja el valor 1 en la variable a y el valor 2 en la variable b.
Sin la desestructuración, para extraer elementos de un array y asignarlos a variables usábamos habitualmente la asignación con índice. Con la desestructuración, en una sola línea podemos extraer los elementos que deseemos.
var data = [4, 8, 15, 16, 23, 42]; // Sin desestructuración var first = data[0]; var second = data[1]; var fourth = data[3]; // Con desestructuración var [first, second,,fourth] = data;
Observa el código de la última línea en el ejemplo anterior. Las dos comas seguidas. Eso permite saltarse un elemento del array que estamos desestructurando. Es decir, first será el primer elemento del array data, second será el segundo, el tercero queda sin asignar (por las dos comas seguidas) y fourth pasa a ser el cuarto elemento de data.
La desestructuración no requiere que haya constantes en el lado derecho de la asignación, funciona con variables también:
var x=10; var y=20; var z=30; var [x,y,z] = [z,x,20];
Al final de este código el valor de:
xes30(el valor anterior dez)yes10(el valor anterior dex)zes20(el valor anterior de la constante)
Pregunta: ¿Cómo intercambiarías el valor de dos variables usando la desestructuración?
Desestructuración en valores de retorno
Si una función devuelve un array, se puede usar la desestructuración para obtener todos o aquellos elementos del array que nos interesen:
var foo = function() {return [1,2,3];} var [a,,b] = foo();
Al final de este código, a vale 1 y b vale 3 (observa las dos comas seguidas).
La desestructuración es segura. Si intentamos desestructurar más elementos de los que hay en el array, los que no existan devuelven undefined.
DEMO: Desestructuración en arrays
La desestructuración se basa en pattern matching.
var arr = [10, 20]; // Lo que nos permite la desestructuración es declarar variables de la siguiente manera var [a, b] = arr; a; // -> 10 b: // -> 20 var [c,d] = [30, 40]; c; // -> 30 d; // -> 40
Esta característica también nos permite intercambiar valores de variables en una única instrucción:
[a, c] = [c, a];
Incluso podemos decidir qué valores recoger. En el siguiente ejemplo cogeremos el primer y último elementos:
arr = [10, 20, 30, 40]; var [i,,,f] = arr; i; // -> 10 f; // -> 40
Incluso podemos hacerlo con arrays más pequeños de lo esperado y no nos dará error:
var [e,,,f] = [10]; e; // -> 10 f; // -> 'undefined'
Desestructuración en objetos
La desestructuración también funciona en objetos. Es decir, se puede desestructurar un objeto y asignar todas o algunas de sus propiedades a variables simples:
var obj = {a: 10, b: 42}; // Sin desestructuración var x = obj.a; var y = obj.b; // Usando desestructuración var {a: x, b: y} = obj;
Al finalizar el código el valor de x es 10 (el valor de la propiedad a del objeto obj). Y el valor de y es 42.
Podemos desestructurar parcialmente un objeto, no necesitamos desestructurar todas sus propiedades:
var {b: x} = {a: 10, b: 42}
Al finalizar el código la variable x vale 42.
Por supuesto, puede combinarse la desestructuración de arrays y objetos:
var data = {a:10, b:[1,2,3, {c: 40, d: 50}]}; var {b: [,x]} = data;
Al finalizar este código el valor de x es 2, que es el segundo elemento del array data.b. Como puedes ver el poder de la desestructuración es enorme.
Desestructuración en parámetros
La gestión de parámetros opcionales en las funciones en JavaScript siempre ha sido problemática. Cierto es que usando typeof y el operador || se puede conseguir mucha flexibilidad, pero el principal problema sigue siendo que viendo la firma de la función uno no sabe qué parámetros pasarle. Mejor veámoslo con un ejemplo:
var foo = function(params) { var uri = params.uri; var method = params.method || 'GET'; var contentType = params.contentType || 'application/json'; // Hacer la petición a la uri indicada con el método y content-type indicados } // Uso de la función: foo({uri: 'http://campusmvp.es'}); // method es GET y contentType es 'application/json' foo({uri: 'http://campusmpv.es', contentType: 'text/html', method: 'GET'});
El principal problema para alguien que quiera llamar a foo es ¿cómo sabe cuáles son las propiedades del objeto que se le debe pasar como parámetro?
No hay más solución que leerse todo el código fuente de la función. Solo así podemos ver que la función foo espera las propiedades uri, method y contentType dentro del objeto. Usar la desestructuración ayuda a explicitar este hecho:
var foo = function({uri: uri, method: method, contentType: contentType}) { // En este punto las variables uri, method y contentType ya existen gracias a la desestructuración }
Para quien llame a foo no hay diferencia entre ambas versiones, pero la segunda deja mucho más claras las propiedades que se esperan en el objeto que se recibe como parámetro. En este ejemplo el nombre de la propiedad (a la izquierda de los dos puntos) y el de la variable local generada por la desestructuración (a la derecha de los dos puntos) son iguales, aunque no tiene por qué. Pero si este es el caso todavía podemos simplificar más el código:
var foo = function({uri, method, contentType}) { // En este punto las variables uri, method y contentType ya existen gracias a la desestructuración }
Recuerda que en ECMAScript 2015 si usamos notación de objeto y una propiedad “x” se inicializa a partir de una variable llamada “x” no tenemos por qué poner {x:x}, sino que podemos poner simplemente {x}.
Incluso podemos ir un paso más allá y dar valores por defecto a las propiedades no pasadas:
var foo = function({uri, method: method='GET', contentType: ctype='application/json'}) { // En este punto las variables uri, method y ctype ya existen gracias a la desestructuración }
En este caso si el llamante no especifica el valor de las propiedades method y contentType éstas toman los valores por defecto.
Observa que cuando usamos los valores por defecto, aunque la variable resultado de la desestructuración se llame igual que la propiedad del objeto, debemos especificar ambos (caso de method en el ejemplo anterior).
