0% encontró este documento útil (0 votos)
16K vistas16 páginas

Desestructuración en Javascript

El documento introduce la desestructuración en JavaScript como una nueva característica de ES6 que permite asignar valores a variables a partir de una estructura de datos dada. Explica que la desestructuración funciona tanto con arrays como con objetos y permite ignorar, procesar y combinar valores. En la segunda parte, el documento presenta ejemplos más complejos que ilustran el poder y flexibilidad de la desestructuración.

Cargado por

Ti Rex Rex
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
16K vistas16 páginas

Desestructuración en Javascript

El documento introduce la desestructuración en JavaScript como una nueva característica de ES6 que permite asignar valores a variables a partir de una estructura de datos dada. Explica que la desestructuración funciona tanto con arrays como con objetos y permite ignorar, procesar y combinar valores. En la segunda parte, el documento presenta ejemplos más complejos que ilustran el poder y flexibilidad de la desestructuración.

Cargado por

Ti Rex Rex
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 16

557621779.

docx 1 de 16

Desestructuración en Javascript. Parte 1


04 Jul 2016

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.
557621779.docx 2 de 16

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:
557621779.docx 3 de 16

 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:
557621779.docx 4 de 16

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.
557621779.docx 5 de 16

Desestructuración en Javascript. Parte 2 (recetas y ejemplos)


07 Jul 2016

Introducción

En el anterior artículo revisamos la sintaxis, teoría y algunos ejemplos de esta nueva funcionalidad en ES6 que es la
desestructuración.
Vamos a completar ahora la teoría con muchos ejemplos, o recetas, más complejos que iremos comentando según
sea necesario. ¡Vamos a ello!

Combinando objetos y arrays


Como ya vimos, la desestructuración puede aplicarse sobre cualquier tipo de colección, ya sea un array, un objeto.
Pero incluso podemos combinar ambos al mismo tiempo:

var { foo: x, bar: [ y, z ] } = { foo: 'Hello World', bar: [ 'Goodbye', 'Lenin' ] };


 
console.info( x, y, z ); // Hello World Goodbye Lenin

Valores por defecto


Podemos indicar valores por defecto para las variables del conjunto de entrada que no encuentren correspondencia
en el conjunto de valores:

var [ foo = 'Hello World', bar = 'Goodbye Lenin' ] = [ 'OK', undefined ];


 
console.info( foo, bar ); // OK Goodbye Lenin
NOTA: En este caso, hay que recordar que ‘undefined’ dispara siempre el valor por defecto, no ocurriendo así con
otros  valores de tipo falsy:

var [ x = 'one', y = 'two', z = 'three' ] = [ null, 'OK', '' ];


console.info( x, y, z ); // null OK (cadena vacía)
 
var [ x = 'one', y = 'two', z = 'three' ] = [ NaN, 'OK', [] ];
console.info( x, y, z ); // NaN OK []

Los valores por defecto, como ocurre en los parámetros de las funciones, pueden ser una función:
var isRequired = ( name ) => { throw new Error( 'Missing parameter: ' + name ); }
 
var [ foo = isRequired( 'foo' ), bar = isRequired( 'bar' ) ] = [ 'OK' ];
 
// Error: Missing parameter: bar

Y también se pueden aplicar sobre el valor de una clave en un objeto:


var obj = {
foo: 'Hello',
bar: 'World'
};
 
var { foo, bar, foobar = 'Default' } = obj;
console.info( foo, bar, foobar ); // Hello World Default
IMPORTANTE: FIREFOX
557621779.docx 6 de 16

Mientras se escribe este artículo, la versión actual de Firefox (la 47), no soporta los valores por defecto para objetos
en determinados escenarios. Tomemos el siguiente ejemplo:

var books = [
{
title: "La vida del lazarillo de Tormes"
}, {
title: "The Never Ending Story",
author: "Michael Ende"
}, {
title: "The Lords of the Rings",
author: "J. R. R. Tolkien"
}, {
title: "Beowulf"
}
];
 
for ( var { title = 'Unknown', author = 'Anonymous' } of books ) {
console.log( title, author );
}

El código anterior reproduce una colección que recoge nombres y autores de libros. Cuando nuestra respuesta no
incluye cualquiera de esas claves, podríamos utilizar las funcionalidades del lenguaje para que Javascript aplique un
nombre por defecto a cualquiera de ellos.
En Chrome, la respuesta sería la esperada:

// La vida del lazarillo de Tormes Anonymous


// The Never Ending Story Michael Ende
// The Lords of the Rings J. R. R. Tolkien
// Beowulf Anonymous

Sin embargo, en Firefox obtenemos un error:


// SyntaxError: destructuring defaults aren't supported in this destructuring declaration

Para que la cosa funcione, tendríamos que reescribir el bucle for of de este modo:
for ( var book of books ) {
( { title = 'Unknown', author = 'Anonymous' } = book );
 
console.info( title, author );
}

Iterando sobre determinados elementos


Volvamos a nuestra lista imaginaria de libros para comprobar cómo podemos recoger solo aquellas claves que
necesitemos mientras iteramos por ella:

var books = [
{
title: "La vida del lazarillo de Tormes",
author: "Anonymous",
published: "1554"
557621779.docx 7 de 16

}, {
title: "The NeverEnding Story",
author: "Michael Ende",
published: "1984"
}, {
title: "The Lord of the Rings",
author: "J. R. R. Tolkien",
published: "1954"
}, {
title: "Beowulf",
author: "Anonymous",
published: "900"
}
];
 
for ( var { title } of books ) {
console.info( title );
}

Extrayendo claves de un objeto en la línea de parámetros de una función


Otra de las posibilidades que ahora nos brindan las funciones en Javascript es poder aplicar la desestructuración
directamente en los parámetros que recibe.
Volvemos a utilizar el array ‘books‘ anterior y generemos por cada libro un título amigable para URLs:

var parseBookTitle = function ( { title = 'Unknown' } ) {


return title.replace( /\s+/g, '-' ).toLowerCase()
};
 
console.info( books.map( parseBookTitle ) );
// [ "la-vida-del-lazarillo-de-tormes", "the-neverending-story", "the-lord-of-the-rings", "beowulf" ]

O si preferimos la sintaxis de las Funciones Flecha, también funciona:

var parseBookTitle = ( { title } ) => title.replace( /\s+/g, '-' ).toLowerCase();

Parámetros de funciones completamente opcionales


En la misma línea del ejemplo anterior, podemos usar la desestructuración para crear Funciones Puras que puedan
funcionar sin parámetros de entrada:

var random = ( { min = 1, max = 999 } = {} ) =>


Math.floor( Math.random() * ( max - min ) ) + min;
 
console.info( random() ); // 845
console.info( random( { min: 10, max: 20 } ) ); // 16
console.info( random( { min: 10, max: 20 } ) ); // 17
console.info( random( { min: 10, max: 20 } ) ); // 13
557621779.docx 8 de 16

Operador de arrastre
Del mismo modo que podemos utilizar el operador de acarreo/arrastre en funciones, puede aplicarse también
durante la desestructuración:

var [ x, ...y ] = 'abc';


 
console.info( x, y ); // a ["b", "c"]

Este esquema se puede forzar aún más:


var [ x, ...[ y, z ] ] = [ 'a', 'b', 'c' ];
 
console.info( x, y, z ); // a b c

Con objetos también funciona:


var obj = {},
foo;
 
[ foo, ...obj.rest ] = [ 'La', 'donna', 'e', 'mobile' ];
 
console.info( foo ); // La
console.info( obj.rest ); // ["donna", "e", "mobile"]

Desestructurando expresiones regulares


¿Necesitamos desestructurar las diferentes partes de una URL? No hay problema; es muy fácil:
var url = 'https://openlibra.com/es/book/learn-to-code-with-scratch';
 
var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec( url );
var [ , protocol, fullhost, fullpath ] = parsedURL;
 
console.info( protocol ); // https:
console.info( fullhost ); // openlibra.com

La idea aquí es utilizar el array de salida generado por la expresión regular y darle nombre a cada una de sus partes
para una reutilización más clara.
O si preferimos montar un objeto que sirva de mapa con sus correspondientes parejas de clave/valor:

var parsedURLObj = {};


 
[ , parsedURLObj.protocol, parsedURLObj.fullhost, parsedURLObj.fullpath ] = parsedURL;
 
console.info( parsedURLObj );
// Object { protocol="https", fullhost="openlibra.com", fullpath="es/book/learn-to-code-with-scratch" }

Swap (o intercambio de valores entre variables)


Gracias a la desestructuración podemos intercambiar valores entre variables sin necesidad de declarar otra temporal
intermedia:
var foo = 'Hello',
bar = 'World';
 
557621779.docx 9 de 16

[ foo, bar ] = [ bar, foo ];


 
console.info( foo ); // World
console.info( bar ); // Hello

Este tipo de estructura es muy interesante cuando trabajamos con algoritmos. Tomemos por ejemplo el
famoso algoritmo de Euclides para calcular el máximo común divisor (mcd); en Javascript tradicional, podríamos
escribirlo de este modo:

function gcd ( a, b ) {
if ( b > a ) {
var temp = a;
a = b;
b = temp;
}
 
while ( true ) {
a %= b;
if ( a === 0 ) { return b; }
b %= a;
if ( b === 0 ) { return a; }
}
}
 
console.info( gcd( 35, 75 ) ); // 5
console.info( gcd( 21, 81 ) ); // 3

NOTA: El código ha sido tomado de  aquí, aunque he suprimido las dos primeras líneas (comprobaciones) para no
hacer el fragmento más largo.
La clave está en el uso de una variable intermedia temp para intercambiar los valores de ‘a’ y ‘b’. Si utilizamos la
desestructuración, el código se reduce del siguiente modo:

function gcd ( a, b ) {
if ( b > a ) {
[ b, a ] = [ a, b ];
}
 
while ( true ) {
a %= b;
if ( a === 0 ) { return b; }
b %= a;
if ( b === 0 ) { return a; }
}
}
 
console.info( gcd( 35, 75 ) ); // 5
console.info( gcd( 21, 81 ) ); // 3
557621779.docx 10 de 16

Desestructurando series infinitas


Próximamente trataré este tema en profundidad pero, como aperitivo, podemos utilizar la desestructuración para
extraer porciones/segmentos dentro de una serie infinita. Esto en algoritmia es una poderosa herramienta que abre
las puertas a la computación paralela y asíncrona.
Tomemos por ejemplo la siempre útil sucesión de Fibonacci, convirtiéndola en una función de tipo Generador. A
continuación, extraigamos por desestructuración solo sus primeros elementos:

var fibonacci = function* () {


var n1 = 1, n2 = 1;
 
while ( true ) {
var current = n2;
n2 = n1;
n1 = n1 + current;
 
yield current;
}
};
 
[ a, b, c, d, e, f ] = fibonacci();
 
console.info( a, b, c, d, e, f );
// 1 1 2 3 5 8

Por supuesto, gracias a la elisión, podemos omitir resultados:


[ , , , , , , a, b, c, d, e, f ] = fibonacci();
 
console.info( a, b, c, d, e, f );
// 13 21 34 55 89 144

Ya que estamos tratando el tema, podemos desestructurar el contenido del bucle ‘while‘ anterior. Perdemos
legibilidad pero nos permitimos esa licencia como un ejercicio para la ocasión:
var fibonacci = function* () {
var n1 = 1, n2 = 1;
 
while ( true ) {
var current = n2;
[ n2, n1 ] = [ n1, ( n1 + current ) ]; // Don't Try This at Home
 
yield current;
}
};

NOTA: Recordad que  no es posible utilizar la sintaxis de las Funciones Flechas cuando estamos trabajando con
Generadores. No podríamos, por ejemplo, escribir algo como lo siguiente:

var fibonacci = *() => {


// Uncaught SyntaxError: Unexpected token *
};
557621779.docx 11 de 16

Conclusión
Con este segundo artículo completamos por el momento este repaso a la desestructuración en el nuevo estándar
ES6. Hemos podido ver varios ejemplos donde esta estructura puede resultar útil, exprimiendo sus posibilidades y
dando una suerte de recetas que pueden ayudarnos a comprenderlas mejor.
No quiero cerrar esta serie sin comentar cómo el uso y abuso de estas estructuras pueden también complicarnos la
vida. Es posible que lleguemos a perdernos en un fragmento de código por culpa de una serie de
instrucciones/asignaciones que no resultan evidentes. Si al final, por culpa de tanto corchete tenemos que abrir la
consola del navegador y comenzar a poner puntos de ruptura para comprobar el valor de nuestras variables a
cada línea, no habremos ganado nada.
La desestructuración es una buena herramienta que ha funcionado perfectamente en otros lenguajes, pero conviene
no volvernos locos. Evitemos sacrificar la legibilidad de nuestros programas solo por el postureo de parecer
modernos. Como siempre digo, lo más inteligente es escribir código pensando que el siguiente programador en
leerlo puede ser un psicópata que sabe dónde vivimos…

El Operador de Propagación en Javascript (ECMAScript 6 y polyfill)


03 Jun 2014

Introducción
En el artículo anterior pudimos ver cómo los ‘rest parameter‘ nos permiten manejar un número indeterminado de
argumentos en nuestras funciones. Pero podemos utilizar este mismo concepto para, en modo inverso, convertir un
array en una serie de argumentos para una función. Para ello, ECMAScript 6 incorpora el Operador de Propagación,
el cual permite exactamente eso.
Veamos cómo utilizarlo…

Ejemplo básico
El Operador de Propagación, o spread operator, se compone del nombre de nuestro array precedido por tres
puntos:
var foo = [ 'En', 'un', 'lugar', 'de', 'la', 'Mancha' ];
 
console.info( ...foo );
// En un lugar de la Mancha

En este sencillo ejemplo, estamos cogiendo nuestro array para procesar cada uno de sus elementos como
argumentos de la propia función ‘console.info’, por lo que el resultado sería equivalente a escribir:

console.info( foo[ 0 ], foo[ 1 ], foo[ 2 ], foo[ 3 ], foo[ 4 ], foo[ 5 ] );

La idea es la expuesta: descomponemos el array en elementos y pasamos cada uno de ellos como argumentos a
una función. La comodidad aquí es que no necesitamos conocer el número de elementos que contiene el array ni
crear bucles que lo recorran, o usar otras funcionalidades del objeto Array.

Concatenar Arrays
Esta funcionalidad del operador de propagación puede ser especialmente útil por ejemplo para concatenar arrays.
Por ejemplo:
var foo = [ 'En', 'un', 'lugar', 'de', 'la', 'Mancha' ],
bar = [ 'de', 'cuyo', 'nombre', 'no', 'quiero', 'acordarme' ],
 
// Old Style
oldStyle = foo.concat( bar ),
 
557621779.docx 12 de 16

// ECMAScript 6 style
ES6Style = [ ...foo, ...bar ];
 
console.info( oldStyle );
// [ "En", "un", "lugar", "de", "la", "Mancha", "de",
"cuyo", "nombre", "no", "quiero", "acordarme" ]
 
console.info( ES6Style );
// [ "En", "un", "lugar", "de", "la", "Mancha", "de",
"cuyo", "nombre", "no", "quiero", "acordarme" ]

Como vemos, hemos utilizado las dos formas: la clásica del concat, y el método más ‘reciente’ (y más legible) que
nos permite ES6.

El funcionamiento en este caso es similar: se han cogido cada uno de los elementos de los arrays que se quieren
procesar para pasarlos como argumentos de un tercero. El resultado lógico es que obtenemos una nueva matriz con
cada uno de los elementos independientes de sus dos fuentes. Simple y elegante!

Uso simple en funciones


Si antes vimos el ejemplo sobre una función ‘predefinida’ como es console, podemos extrapolar el ejemplo a una
función personalizada. Pensemos por ejemplo en una función que espera una serie de parámetros para guardar un
objeto en una base de datos:

var saveBook = function ( title, author, publisher ) {


/* Awesome logic goes here... */
console.info(
'The book ', title,
' by ', author,
' published by ', publisher,
' has been added to database!'
);
}
 
var book1 = [ 'JavaScript: The Good Parts', 'Douglas Crockford', 'OReilly' ],
book2 = [ 'JavaScript: The Definitive Guide', 'David Flanagan', 'OReilly' ];
 
saveBook( ...book1 );
// The book JavaScript: The Good Parts by Douglas Crockford
// published by OReilly has been added to database!
 
saveBook( ...book2 );
// The book JavaScript: The Definitive Guide by David Flanagan
// published by OReilly has been added to database!
El ejemplo juega con este operador de propagación para tomar una matriz (un array) y convertirlo en argumentos
independientes para nuestra función saveBook.
Si pasamos más elementos en el array de los que espera la función, no ocurre ningún error:
var book3 = [ 'Eloquent JavaScript', 'Marijn Haverbeke',
'No Starch Press', '2011', 'English' ];
 
saveBook( ...book3 );
// The book Eloquent JavaScript by Marijn Haverbeke
// published by No Starch Press has been added to database!
557621779.docx 13 de 16

Si por el contrario omitimos valores, el intérprete Javascript los reemplazará por ‘undefined’, pero no lanzará un
mensaje de error:
var book4 = [ 'JavaScript Patterns' ];
 
saveBook( ...book4 );
// The book JavaScript Patterns by undefined
// published by undefined has been added to database!

Este último ejemplo, que podría provocar errores o incosistencias, podría quedar mucho más completo si añadimos
a nuestros argumentos un valor por defecto tal y como vimos en el post Cómo asignar valores por defecto a los
argumentos de una función (revisión ES6):

var saveBook = function ( title = 'Unknown', author = 'Unknown', publisher = 'Unknown' ) {


/* Awesome logic goes here... */
console.info(
'The book ', title,
' by ', author,
' published by ', publisher,
' has been added to database!'
);
}

Por lo que ahora, si omitimos argumentos, obtenemos al menos un valor por defecto en lugar del problemático
‘undefined’:
var newBook = [ 'Node.js in Action' ];
 
saveBook( ...newBook );
// The book Node.js in Action by Unknown published by Unknown has been added to database!

¿Y si el objeto que pasamos no es iterable?


En ese caso, el intérprete nos dará un error interrumpiendo la ejecución de nuestro programa:

var foo = { foo: 'Hello World', bar: 'Goodbye Lenin' };


 
console.info( ...foo );
// TypeError: foo is not iterable

Para evitar que un error de este tipo pueda bloquear nuestra aplicación, habría quizá que recurrir a algún tipo de
comprobación previa, o a coercionar el tipo de la variable sobre la que queremos operar… Por ejemplo:
var foo = { foo: 'Hello World', bar: 'Goodbye Lenin' },
bar = [ 'La', 'donna', 'e', 'mobile' ];
 
// Checking
foo.length && ( console.info( ...foo ) ); // Nothing happens
bar.length && ( console.info( ...bar ) ); // La donna e mobile
 
// Coercing
foo.length || ( foo = [] );
console.info( ...foo ); // (nothing to show)
557621779.docx 14 de 16

No son soluciones a priori elegantes, pero evitamos así el error: comprobamos si la variable es ‘iterable’
preguntando por su atributo ‘length’; en caso de que no lo posea, o bien no llamamos a nuestra función (primer
método del ejemplo), o bien reescribimos su valor por un array vacío (segundo método).

¿Operador de Propagación en ambos lados?


Si hemos visto que este nuevo operador se puede usar tanto en los argumentos de las funciones como en las
llamadas, ¿es posible usarlo en ambos lados al mismo tiempo? Posible es: no tiene ninguna utilidad aparente, pero
se puede.
Solo a modo de ejemplo:
var findMaxValueIn = function ( ...numbers ) {
return Math.max.apply( Math, numbers );
};
 
var values = [ 20, 40, 10, 30 ];
 
console.info( findMaxValueIn( ...values ) ); // 40

El sinsentido del ejemplo es que convertimos un array en valores individuales para luego volverlos a recomponer. Lo
cierto es que no encuentro ningún escenario donde esto podría ser interesante, pero aquí queda como

demostración de la flexibilidad del lenguaje 

Polyfill
Como viene siendo habitual, para aquellos navegadores antiguos que no soporten la nueva especificación, hay que
modificar este operador de propagación por su forma extendida. En este caso, sin embargo, es más sencillo que con
funcionalidades más complejas. Basta modificar la llamada simple a nuestra a nuestra función y recurrir al
método apply. Volviendo a nuestro ejemplo anterior de saveBook, basta con:

saveBook.apply( null, book );


Resulta menos intuitivo y legible, pero nos puede sacar de un apuro si el navegador no soporta la nueva sintaxis.

Conclusión
El operador de propagación puede ser una herramienta interesante, y muy potente, para trabajar con funciones que
reciben parámetros desde fuentes externas. Puede ser por ejemplo muy útil cuando trabajamos con la respuesta
que nos proporciona una API de terceros, o para manipular los datos que recogemos de un formulario. Usado de
forma más exótica, también nos permite concatenar arrays…
Como siempre se dice por aquí, cualquier innovación en el lenguaje es bienvenida, y esta no va a ser menos!

Los «Rest Parameters» en Javascript (ECMAScript 6 y polyfill)


22 May 2014

Una de las nuevas funcionalidades que nos permite ECMAScript 6 es el poder agrupar los argumentos que llegan a
nuestra función en un array. Es lo que se conoce como Rest Parameters, y aunque está estrechamente relacionado
con el objeto arguments que ya hemos visto en varias ocasiones (aquí o aquí), presenta algunas diferencias.
Echemos un vistazo!

Sintaxis básica
La forma de implementar esta funcionalidad es sencilla y recuerda a otros lenguajes como Ruby:
var myFunc = function ( foo, bar, ...theArgs ) {
// ...
}
557621779.docx 15 de 16

Esta función, con sus peculiares tres puntos delante del tercer argumento, estarían indicándole al intérprete que ese
valor debe ser un array compuesto por los parámetros que lleguen desde la llamada siguiendo la siguiente lógica:
 El primer parámetro que llegue, se mapea como foo.
 El segundo parámetro que llegue, se mapea como bar.
 El resto de parámetros, se guardarán dentro de un array definido como theArgs.

Así, el ejemplo desarrollado:


var myFunc = function ( foo, bar, ...theArgs ) {
console.info( 'foo: ', foo );
console.info( 'bar: ', bar );
console.info( 'theArgs: ', theArgs );
};
 
myFunc( 'myParam1', 'myParam2', 'myParam3', 'myParam4', 'myParam5' );

Daría como resultado dentro de la función que:


foo: myParam1
bar: myParam2
theArgs: ["myParam3", "myParam4", "myParam5"]

Un detalle interesante es que una vez que utilizamos esta funcionalidad, dejamos de tener acceso al objeto
arguments. Si preguntamos dentro de la función por dicho objeto, obtendríamos el siugiente mensaje de error:
SyntaxError: 'arguments' object may not be used in conjunction with a rest parameter

Un verdadero Array
Una característica importante es que estos ‘rest arguments’ si constituyen un verdadero array, a diferencia de
arguments, que dan lugar a un «objeto similar a un array» que tiene algunas de sus propiedades como length pero
que carece de otras como shift o pop.

Hagamos una comprobación rápida de los dos tipos de objeto:


var myArgsFunc = function () {
console.info( Object.prototype.toString.call( arguments ) );
};
 
var myRestFunc = function ( ...myArgs ) {
console.info( Object.prototype.toString.call( myArgs ) );
};
 
myArgsFunc( 'myParam1', 'myParam2', 'myParam3', 'myParam4' ); // [ Object Arguments ]
myRestFunc( 'myParam1', 'myParam2', 'myParam3', 'myParam4' ); // [ Object Array ]

Como vemos, en la segunda si tenemos un verdadero array, lo que nos permite evitar snippets como el que hemos
usado hasta ahora del tipo:
var myArgsFunc = function () {
var args = Array.prototype.slice.call(arguments);
console.info( Object.prototype.toString.call( args ) );
};
 
myArgsFunc( 'myParam1', 'myParam2', 'myParam3', 'myParam4' ); // [ Object Array ]
557621779.docx 16 de 16

NOTA: Para más información sobre el método Object.prototype para comprobar el tipo de objetos, ver el artículo
«Cómo obtener el tipo de datos preciso de una variable en Javascript«.

Otro detalle importante, es que no podemos asignar un valor por defecto a este tipo de objeto, ya que en caso de
omisión, el intérprete ya asigna un array vacío:
var myRestFunc = function ( ...myArgs ) {
console.info( myArgs );
};
 
myRestFunc(); // []

Si intentamos forzarlo con la también nueva funcionalidad ECMAScript 6 que ya comentamos aquí, obtendremos un
error:
var myRestFunc = function ( ...myArgs = [ 'default1', 'default2' ] ) {
//...
};
 
myRestFunc(); // SyntaxError: rest parameter may not have a default

Polyfill
Para seguir la costumbre, mostramos a continuación el polyfill para aquellos navegadores que aún no interpreten el
estándar ES6:
var myPolyfillRestFunc = function ( x /* ...y */ ) {
for ( var y = [], _y = 1; _y < arguments.length; _y++ ) {
y[ _y - 1 ] = arguments[ _y ] ;
}
 
console.info( 'x: ', x );
console.info( 'y: ', y );
};
 
myPolyfillRestFunc( 'myParam1' );
// x: myParam1
// y: []
 
myPolyfillRestFunc( 'myParam1', 'myParam2', 'myParam3', 'myParam4', 'myParam5' );
// x: myParam1
// y: ["myParam2", "myParam3", "myParam4", "myParam5"]
La magia la hacemos, como no, a través del viejo arguments a partir del cual, gracias a un bucle, vamos rellenando
nuestro array.

Conclusión
Esta nueva funcionalidad viene a cubrir un aspecto del lenguaje que hasta el momento tenía que solucionarse con
métodos cuanto mínimo exóticos. Puede que a priori no recurramos demasiado a esta forma de recoger los
argumentos de nuestras funciones, o le encontremos directamente utilidad, pero el hecho de que podamos hacerlo
en caso necesario de forma nativa, siempre es positivo.

También podría gustarte