Funciones flecha o Arrow functions en JavaScript

Funciones flecha o Arrow functions en JavaScript

Las funciones flecha o arrow funtions son funciones anónimas con una sintaxis más compacta y que aparte de la diferencia en la sintaxis también tienen algunas peculiaridades como que no vinculan su propio this o que no se pueden usar como constructores.

Sintaxis de las funciones fecha

La sintaxis de las funciones flechas es un poco peculiar y para liarlo un poco más hay partes que son opcionales o necesarias dependiendo de los parámetros, el cuerpo y lo que retorne la función como resultado.

Vamos a empezar por definir una función sencilla de la forma clásica para sumar 2 números y vamos a ver cómo convertirla en una función flecha.


function suma(numero1, numero2) {
  return numero1 + numero2;
}

Sabemos que es una función precisamente porque lleva la palabra function, pues con las arrow funtions lo primero que tenemos que tener claro es que no llevan ni la palabra function ni ninguna otra, la forma por la que vamos a ver que es una función flecha es porque va a tener el operador flecha => entre los parámetros y el cuerpo de la función, por lo tanto la primera sintaxis posible para nuestra función flecha es sustituir function por =>.

Las funciones flecha son funciones anónimas y por lo tanto si queremos poder llamar a nuestra función debemos de asignársela a una variable, y aunque esta asignación no forma parte de la definición de una función flecha en la práctica lo tendremos que hacer siempre que no estemos asignando la función flecha a otra función directamente.


var suma = (numero1, numero2) => {
  return numero1 + numero2;
}

Esta primera sintaxis no parece ser de mucha utilidad e incluso puede parecer hasta peor pero si como en este caso el cuerpo de la función solo tiene una sentencia podemos eliminar el return y las {} y ya tenemos una sintaxis mucho más compacta, ideal para usar en los callbacks por ejemplo.


var suma = (numero1, numero2) => numero1 + numero2

Por lo tanto podemos ver las funciones flecha como un flujo en el que dados unos parámetros (parte izquierda) nos devuelve un resultado (parte derecha).


// Asignamos la funcion a una variable para reutilizarla
var suma = (numero1, numero2) => numero1 + numero2
var a = suma(2, 3);
// 5
var b = suma(4, 5);
// 9

// O la usamos directamente
function calcular(operacion, numero1, numero2) {
  return operacion(numero1, numero2);
}

var c = calcular((numero1, numero2) => numero1 + numero2, 6, 7);
console.log(c);
// 13

Sintaxis de las funciones fecha con solo parámetro

Si la función tiene un solo parámetro entonces también se pueden eliminar los paréntesis de los parámetros.


var saludar = nombre => console.log(`Hola, ${nombre}`)
console.log(saludar("Iván"));
// Hola, Iván

Sintaxis de las funciones fecha sin parámetros

Con un parámetro podemos quitar los paréntesis pero si la función no recibe parámetros vuelven a ser igual de obligatorios que cuando tenemos varios parámetros.


var mostrarFecha = () => new Date().toLocaleDateString()
console.log(mostrarFecha());
// 1/5/2019

Sintaxis de las funciones fecha que devuelve un objeto literal

Si en la función flecha queremos devolver un objeto directamente vamos a tener que envolver el cuerpo de la función entre paréntesis aunque resulte raro, el motivo es que si no lo hiciésemos se interpretaría el objeto como el cuerpo de la función y no sería un código javaScript valido.


var usuario = (nombre, apellido) => ({nombre: nombre, apellido: apellido})
console.log(usuario("Iván", "Salas"));
// Object { nombre: "Iván", apellido: "Salas" }

Parámetros rest en las funciones flecha

Los parámetros rest se pueden usar en las funciones flecha sin ningún problema de la misma forma que en las funciones clásicas.


// Forma clasica
function miFuncion(parametro1, parametro2, ...otrosParametros) {
  return parametro1 + parametro2 + otrosParametros.reduce(
    function sumar(numero1, numero2) {
      return numero1 + numero2;
    });
}

console.log(miFuncion(1, 2, 3, 4));
// 10

// Con funciones flecha
var miFuncionFlecha = (parametro1, parametro2, ...otrosParametros) =>
  parametro1 + parametro2 + otrosParametros.reduce((numero1, numero2) => numero1 + numero2);

console.log(miFuncionFlecha(1, 2, 3, 4));
// 10 

Cuando la función se hace muy larga como en este último ejemplo normalmente incluimos saltos de línea para hacerlas más legibles, con las funciones flecha también podemos hacerlo siempre que no lo hagamos entre los parámetros y la flecha.

This y las funciones flecha

Uno de los problemas clasicos a los que nos enfrentamos en javaScript es el uso de this dentro de funciones porque la forma en la que funciona parece ir en contra de lo que esperaríamos, vamos a verlo con un ejemplo.

Definimos una función que tiene una variable y mediante setInterval la vamos actualizando cada 1s y esperamos que si consultamos esa variable después de 10s por ejemplo el valor sea 10 pero nada más lejos de la realidad, siempre se va a quedar en 0.


function Cronometro() {
  this.tiempo = 0;

  setInterval(function() {
    this.tiempo++;
  }, 1000);
}

var c = new Cronometro();

// Consultamos el valor cada 1s 
setInterval(() => console.log(c.tiempo), 1000);
// 0
// 0
// ...

Esto es porque el objeto this no hace referencia al objeto this del objeto cronometro si no al this del objeto desde el que se invoca (Window) que no tiene definida la variable tiempo, si le metemos un log comprobamos que es eso lo que está pasando.


function Cronometro() {
  this.tiempo = 0;

  setInterval(function() {
    console.log(this.tiempo, this);
    this.tiempo++;
  }, 1000);
}

var c = new Cronometro();

// Consultamos el valor cada 1s 
setInterval(() => console.log(c.tiempo), 1000);
// undefined, Window
// NaN, Window
// NaN, Window
// ...

Y si añadimos una variable global tiempo «si funciona» pero con el tiempo que no corresponde porque el this dentro del setInterval es distinto de el del objeto Cronometro, para solucionar este problema podemos usar el método .bind() o guardar su valor en una variable y utilizarla en su lugar (el típico var self = this; que se solía utilizar antes).


function Cronometro() {
  this.tiempo = 0;

  setInterval(function() {
    console.log('tiempo interno: ' + this.tiempo);
    this.tiempo++;
  }, 1000);
}

var tiempo = 100;
var c = new Cronometro();

// Consultamos el valor cada 1s 
setInterval(() => console.log('tiempo externo: ' + c.tiempo), 1000);
// tiempo interno: 100
// tiempo externo: 0
// tiempo interno: 101
// tiempo externo: 0
// tiempo interno: 102
// tiempo externo: 0
// ...

Con las funciones flecha no tenemos este problema porque no vinculan su this con el del contexto en el que se invocan ya que utilizan el this del contexto en el que están definidas que en este caso sería el del objeto y por lo tanto ahora sí que nos va a funcionar tal y como podríamos esperar.


function Cronometro() {
  this.tiempo = 0;

  setInterval(() => this.tiempo++, 1000);
}


var c = new Cronometro();

// Consultamos el valor cada 1s 
setInterval(() => console.log(c.tiempo), 1000);
// 1
// 2
// 3
// ...

Es decir, el objeto al que hace referencia el this en una función flecha siempre va a ser el mismo independientemente del lugar desde el que se invoque, vamos a comprobarlo añadiendo un log también dentro de la función y una variable tiempo global para ver que es cierto.


function Cronometro() {
  this.tiempo = 0;

  setInterval(() => {
    console.log('tiempo interno: ' + this.tiempo);
    this.tiempo++;
  }, 1000);
}

var tiempo = 100;
var c = new Cronometro();

// Consultamos el valor cada 1s 
setInterval(() => console.log('tiempo externo: ' + c.tiempo), 1000);
// tiempo interno: 0
// tiempo externo: 1
// tiempo interno: 1
// tiempo externo: 2
// tiempo interno: 2
// tiempo externo: 3
// tiempo interno: 3
// ...

This y las funciones flecha en métodos

Todo parece muy bonito con this y las funciones flecha pero hay un caso en el que no las podemos utilizar, y es que si usamos una arrow function como método de un objeto tenemos el efecto contrario al que hemos visto antes.


var mueble = {
  nombre: 'silla',
  getNombreFlecha: () => console.log(this.nombre),
  getNombreNormal: function() {
    console.log(this.nombre);
  }
}

mueble.getNombreFlecha();
// undefined

mueble.getNombreNormal();
// silla

var nombreMueble = mueble.getNombreNormal();
console.log("nombreMueble: " + nombreMueble);
// nombreMueble: undefined

Aquí todo va bien con las funciones normales porque invocamos a la función desde el objeto y por eso el this es el del objeto, siempre que no se nos ocurra asignar la función a otra variable porque entonces estamos cambiando el contexto desde el que se ejecutará y por lo tanto el valor de this y ya tenemos un undefined.

Y con la función flecha también tenemos un undefined porque el contexto en el que está declarada la función es el global porque un objeto no define su propio ámbito.