Cómo asignar valores por defecto a los argumentos de una función (revisión ES6)

19 May 2014

Introducción

Hace ya tiempo le dediqué una entrada en el blog a cómo asignar valores por defecto a los argumentos de una función en Javascript. Es cierto que hasta ahora, cualquiera de los métodos que habíamos usado para conseguir esta funcionalidad básica resultaban poco elegantes y de difícil legibilidad, algo que ha cambiado gracias a la propuesta del ECMAScript 6 y su implementación nativa.

Echemos un vistazo a cómo trabajar desde ya con los nuevos métodos.

Método clásico

Hasta el momento, era relativamente habitual manejar el valor por defecto de los argumentos o parámetros de nuestras funciones mediante una comprobación de tipo cortocircuito:

var myFunc = function ( foo, bar ) {
    foo = foo || 'defaultValueForFoo';
    bar = bar || 'defaultValueForBar';
 
    // ...
    console.info( foo, bar );
};
 
myFunc( 'one', 'two' ); // one two
myFunc(); // defaultValueForFoo defaultValueForBar

Con el código anterior comprobamos si tanto ‘foo‘ como ‘bar‘ traen algún valor para, en caso contrario, asignarle uno por defecto. Un problema que tiene este método es que siempre se realiza una asignación sobre las variables, ya vengan estas con valor o no. Eso consume memoria, por lo que un poco mejor que lo anterior es:

var myFunc = function ( foo, bar ) {
    foo || ( foo = 'defaultValueForFoo' );
    bar || ( bar = 'defaultValueForBar' );
 
    // ...
    console.info( foo, bar );
};

De este modo, la asignación se realiza únicamente cuando falte algún valor.

Nuevo método nativo en ECMAScript 6

Gracias a las mejoras que introduce el nuevo estándar, Javascript podrá manejar de forma nativa los valores de nuestros argumentos como en muchos otros lenguajes de programación llevamos haciendo años:

var myFunc = function ( foo = 'defaultValueForFoo', bar = 'defaultValueForBar' ) {	
    console.info( foo, bar );
};

Seguro que esa estructura resulta familiar a más de uno. Probemos a forzar un array como valor en lugar de una cadena:

var myFunc = function ( x, y = [] ) {
    console.info( 'x: ', x, 'y: ', y, Array.isArray( y ) );
};
 
myFunc( 'Hello World', [ 'one', 'two', 'three' ] );
// x: Hello World y: ["one", "two", "three"] true
 
myFunc( 'Hello World' );
// x: Hello World y: [] true

Efectivamente vemos que el valor asignado en caso de omisión es un array y que la cosa funciona como se espera 🙂

Polyfill para la comprobación simple

Como estamos hablando de ES6 y siempre habrá quien comente que no todos los navegadores lo soportan a día de hoy (IE<11), podemos usar un polyfill para esos casos:

var myFunc = function ( x ) {
    var y = arguments[ 1 ] !== ( void 0 ) ? arguments[ 1 ] : [];
 
    console.info( 'x: ', x, 'y: ', y, Array.isArray( y ) );
};

Con esa comprobación basada en ‘arguments‘, podemos forzar el valor de ‘y‘. Observad que la asignación se hace en el último pase del ternario, donde podríamos especificar que en lugar de un array, fuera un objeto, o una cadena:

// An array
var y = arguments[ 1 ] !== ( void 0 ) ? arguments[ 1 ] : [];
 
// An Object
var y = arguments[ 1 ] !== ( void 0 ) ? arguments[ 1 ] : {};
 
// A String
var y = arguments[ 1 ] !== ( void 0 ) ? arguments[ 1 ] : 'A String';

Comprobación con una función externa

Si Javascript nos permite fijar un valor por defecto a un argumento, podemos ir un poco más allá de la simple asignación y en lugar de eso, llamar a una función. Con ese tipo de estructura, conseguimos comportamientos interesantes:

var checkMissing = function () {
    var message = 'Missing parameter!';
 
    // throw new Error( message );
    console.error( message );
};
 
var myFunc = function ( x, y = checkMissing() ) {
    console.info( 'x: ', x, 'y: ', y );
};
 
myFunc( 'Hello World', [ 'one', 'two', 'three' ] ); 
// x: Hello World y: ["one", "two", "three"]
 
myFunc( 'Hello World' );
// ERROR: Missing parameter!
// x: Hello World y: undefined

Veamos: cuando no encontramos un valor para ‘y‘, llamamos a una función ‘checkMissing‘ la cual se encarga de lanzar un error en la consola. Además, podría gestionar excepciones vía ‘throw‘, como la que se ha comentado en el código anterior.

Un problema aquí puede ser que, aunque capturamos el error, en nuestra función el valor de ‘y‘ continúa siendo ‘undefined‘, lo que provocaría seguramente errores.

Para redondear la comprobación, podemos utilizar la misma asignación de parámetros por defecto para advertir en el error el tipo de parámetro que se está esperando y hacer la aplicación mucho más sólida:

var checkMissing = function ( type = 'unknown' ) {
    var message = 'Missing parameter! We expect: ' + type;
 
    // throw new Error( message );
    console.error( message );
 
    return type;
};
 
var myFunc = function ( x, y = checkMissing( [] ) ) {
    console.info( 'x: ', x, 'y: ', y );
};
 
myFunc( 'Hello World', [ 'one', 'two', 'three' ] );
// x: Hello World y: ["one", "two", "three"]
 
myFunc( 'Hello World' );
// ERROR Missing parameter! We expect:
// x: Hello World y: []

El cambio aquí está en que ‘checkMissing‘ maneja ahora los tipos que se esperan para nuestros “argumentos requeridos”: cuando en ‘myFunc‘ necesitamos que ‘y‘ sea por ejemplo un array, lo indicamos a la funcíon ‘checkMissing‘ la cual, además de lanzar los errores, asigna por defecto dicho valor. Si no pasamos nada, se devuelve el tipo ‘unknown‘ que hemos marcado como valor por defecto para ‘type‘. Lógicamente, podemos forzar que se devuelva un objeto, una cadena, o lo que nos interese:

// An object
var myFunc = function ( x, y = checkMissing( {} ) ) { /* ... */ };
 
// A string
var myFunc = function ( x, y = checkMissing( 'DefaultValueForY' ) ) { /* ... */ };

Polyfill para funciones externas

Igual que hicimos antes para valores simples, añadimos aquí el polyfill para la llamada a una función:

var checkMissing = function () {
    var message = 'Missing parameter!';
 
    console.error( message );
};
 
var myFunc = function ( x ) {
    var y = arguments[ 1 ] !== ( void 0 ) ? arguments[ 1 ] : checkMissing();
 
    console.info( 'x: ', x, 'y: ', y );
};

Fácil: igual que en los casos anteriores, en lugar de forzar un valor, lo que hacemos es llamar a nuestra función externa que nos gestiona el error.

Conclusión

Gracias a ECMAScript 6, el manejo valores por defecto en los parámetros de una función se vuelve algo mucho más limpio y funcional que en las versiones anteriores. Aprovechando además esa nueva sintaxis, hemos vistoque resulta relativamente simple el crear funciones de tipo ‘polyfills’ para aquellos navegadores que aún no soporten el nuevo estándar.

Ya no hay excusas para organizar un poquito mejor nuestros códigos!

Más:

Solo un marciano comentando!

  1. Krobing

    Felicidades por este buen post, ya que vendría siendo la forma mas adecuada de asignar valores por defecto a una función en javascript, anteriormente usaba las que se explicaron en un post anterior, pero ahora voy a usar las nuevas, empezando por los polyfill primero para mantener la compatibilidad.

Deja un comentario

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