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 ) {
    // ...
}

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 veradero 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 ]

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.

Más:

Aún no tenemos debug!
Ningún marciano se ha comunicado.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *