¡Hola! Me parece que andas usando un bloqueador de anuncios =( ¿Nos desbloqueas? ¿Por qué?
F13

Funcion 13

Aprende ES6 - Las funciones flecha

Este es el segundo artículo en el que hablamos de ES6. En artículos anteriores hemos hablado sobre let, const y los ámbitos. Si esto no te suena de nada y estás intentando ponerle algún sentido a las siglas, ¡no te preocupes! Echa un vistazo a nuestra Introducción a ES6.

Hasta ahora, cada vez que usamos nueva sintaxis de ES6 en los artículos, he evitado usar las funciones flecha ya que no me gusta usar sintaxis que no ha sido explicada.

Las funciones flecha son sin duda una de las nuevas adiciones más conocidas de ES6. Si sois habituales de otros lenguajes quizá os sea familiar la sintaxis y, por otro lado, cuentan que la sintaxis viene prestada de aquella aberración conocida como CoffeeScript.

La sintaxis es bastante expresiva aunque en ciertas cosas se sale de lo que estamos acostumbrados a esperar de JavaScript.

Veamos una función flecha en todo su esplendor:

let es6 = [  
  'Funciones Flecha',
  'Let y Const',
  'Clases'
];

es6.forEach(cosa => {  
  console.log('%s son geniales', cosa);
});

// Funciones Flecha son geniales
// Let y Const son geniales
// Clases son geniales

La sintaxis de las funciones flechas van cambiando no obstante así que vamos a verlas una a una.

Funciones sin argumentos

let funcion = () => { return 'Función sin argumentos'; }  

Las funciones sin argumentos solo necesitan los paréntesis.

Funciones que solo devuelven

Si la función sólo va a devolver un valor, no es necesario incluir ni los corchetes ni la palabra clave return:

let funcion = () => 'Función sin argumentos';  

El código compilado es algo así:

var funcion = function funcion() {  
    return 'Función sin argumentos';
};

¿Qué pasa si queremos volver un objeto?

let funcion = () => { cadena: 'Función sin argumentos' };

// undefined :(

Esto ocurre porque realmente estamos añadiendo las llaves por lo que JavaScript intenta evaluar cadena: 'Función sin argumentos' lo cual no es del todo válido.

Si queremos devolver un objeto con esta sintaxis, necesitamos usar los paréntesis:

let funcion = () => ({ cadena: 'Función sin argumentos' });

/*
{ 
  cadena: 'Función sin argumentos'
}
*/

¡Perfecto!

Funciones con un solo argumento

Las funciones que sólo tienen un argumento, no necesitan los paréntesis:

let cuadrados = [2, 3, 4];  
cuadrados.map(num => num * num);

// 4, 9, 16

Como ves es algo bastante útil y que nos ahorra bastante caracteres.

Funciones con varios argumentos

Las funciones que tienen varios argumentos necesitan los paréntesis. Ten en cuenta que si la función solo devuelve un valor, sigue sin hacer falta poner las llaves:

let numeros = [2, 3, 4];  
numeros.map((num, i) => num * indice);

// 0, 3, 8

No obstante, cuando tenemos que usar la sintaxis completa, a la vez que varios argumentos, por ejemplo:

let numeros = [1, 2, 3];

numeros.forEach((num, indice) => {  
  console.log('%d - %d', indice, num);
});

// 0 - 1
// 1 - 2
// 2 - 3

Es probable que debieras usar una función con nombre por varios motivos:

  • La declaración (num, indice) => es tan solo seis caracteres más corta que function (num, indice).
  • Usando la otra forma, te permite darle nombre al método lo cual ayuda en legibilidad y depuración.
  • Cuando ya hay varios argumentos y varias sentencias, es improbable que seis caracteres vayan a importar demasiado mientras que añadirle el nombre podría ser realmente beneficioso.

Las funciones flechas y this

this es siempre un tema problemático en JavaScript y del que ya hablamos algo en este artículo.

Veamos un ejemplo de algo que podríamos solucionar con las funciones flecha. Este código es un sencillo temporizador que cambia el texto de todos los elementos con clase .tiempo por el tiempo UNIX cada segundo.

$('.tiempo').each(function () {
  setInterval(function () {
    $(this).text(Date.now());
  }, 1000);
});

No obstante, al entrar en el callback de setInterval, conseguimos un nuevo this. La forma habitual de solucionar esto (en ES5) es una de estas dos:

// Truco de self
$('.tiempo').each(function () {
  var self = this;

  setInterval(function () {
    $(self).text(Date.now());
  }, 1000);
});

// Con bind
$('.tiempo').each(function () {  
  setInterval(function () {
    $(this).text(Date.now());
  }.bind(this), 1000);
});

Sin embargo, las funciones flecha no crean un nuevo contexto por lo que this, no cambia:

$('.tiempo').each(function() {
  setInterval(() => $(this).text(Date.now()), 1000);
});

Mucho mejor, ¿no?

No obstante, hay que tener cuidado con las funciones flecha y this... también. Veamos este sencillo caso:

(function() {
  this.prueba = 'En la función';

  var foo = {
    prueba: 'En el objeto'
  };

  // Un método que crea métodos
  foo.metodo = function(nombre, fn){
    this[nombre] = fn;
  };

  foo.metodo('bar', () => {
    // ¿Qué pasará?
    console.log(this.prueba); 
  });

  foo.bar();
})();

Dado que estamos dentro de foo, debería ser En el objeto, ¿no? Pues no. Es En la función. Si habéis prestado atención sabréis porqué.

Las funciones flechas de ES6 usan el contexto léxico, (no crean this). Básicamente es una forma engorrosa de decir que usa el valor de this que tenga a su alrededor, el que contenga al código en cuestión.

En este caso, la llamada al método foo.metodo no hay otro código que lo rodee por lo que el valor de this será el valor de la función global (en este caso).

¡Así que mucho ojo!

Conclusiones

Las funciones flecha son una forma abreviada y bonita de definir funciones anónimas pudiendo ahorrarte bastante código en muchas situaciones:

// ES5
function obtenerTokenVerificado(selector) {  
  return obtenerUsuarios(selector)
    .then(function (usuarios) { return usuarios[0]; })
    .then(verificarUsuario)
    .then(function (usuario, tokenVerificado) { return tokenVerificado; })
    .catch(function (err) { log(err.stack); });
}

// Funciones flecha!
function obtenerTokenVerificado(selector) {  
  return obtenerUsuarios(selector)
    .then(usuarios => usuarios[0])
    .then(verificarUsuario)
    .then((usuario, tokenVerificado) => tokenVerificado)
    .catch(err => log(err.stack));
}

No obstante no hay motivo para convertir todas las funciones a funciones flecha a no ser que tenga sentido sintáctico, ya que añadir un nombre a una función puede ser útil en muchas situaciones.

Por otro lado, hay que tener algo de cuidado con el valor de this en las funciones flecha aunque te pueden sacar de algún que otro apuro.

Programador Front-end en First + Third y Potato. Trabajando con JavaScript y HTML5 desde el corazón de Sevilla.

Comentarios ¡Únete a la conversación!