Una forma más corta de escribir funciones
Las arrow functions (o funciones flecha) son la sintaxis más compacta que tiene JavaScript para declarar una función. Aparecieron con ES2015 y hoy son prácticamente el estilo por defecto cuando necesitas funciones pequeñas e inline: callbacks de métodos de arrays, manejadores de promesas o listeners de eventos.
La estructura es sencilla: parámetros, luego => y, por último, el cuerpo.
Mismo resultado que en su forma clásica:
const add = function (a, b) {
return a + b;
};
Escribir menos código está bien, pero lo que realmente hace interesantes a las funciones flecha es el return implícito y cómo manejan this. Vamos a ver ambos puntos.
Return implícito en funciones flecha
Cuando el cuerpo de la función es una sola expresión, puedes omitir las llaves y el return. El valor de esa expresión se devuelve de forma automática:
Aquí es donde las funciones flecha brillan de verdad: las transformaciones de una sola línea se leen casi como fórmulas matemáticas:
En cuanto necesitas más de una instrucción, vuelves a las llaves y al return explícito:
Olvidar el return dentro de las llaves es el error más común con las funciones flecha. x => { x * 2 } devuelve undefined: las llaves convierten el cuerpo en un bloque y la expresión de dentro se descarta sin más.
Paréntesis en los parámetros
Cuando hay un único parámetro, los paréntesis son opcionales:
Cuando hay cero, dos o más parámetros, los paréntesis son obligatorios:
Hay equipos que prefieren poner siempre los paréntesis por consistencia. Ambos estilos son válidos — elige uno y mantenlo.
Devolver un objeto literal desde una arrow function
Ojo con este detalle. Si intentas devolver un objeto de forma implícita, las llaves se confunden con el cuerpo de la función:
Eso imprime undefined. JavaScript interpreta { name: name } como un bloque con una sentencia etiquetada, no como un literal de objeto. Envuelve el objeto entre paréntesis para forzar que se evalúe como una expresión:
Los paréntesis () alrededor de { ... } son la solución. Guárdatelo bien: te vas a topar con esto durante la primera semana que escribas funciones flecha.
this léxico en arrow functions
La verdadera razón de existir de las funciones flecha no es su sintaxis más corta, sino que no tienen su propio this. Lo heredan del ámbito que las rodea.
Para ver por qué esto importa, fíjate en una función normal usada como callback:
Dentro de la function () { ... } que le pasamos a setInterval, this no apunta a counter. Las funciones normales tienen su propio this según cómo se las invoque, y setInterval llama al callback con this valiendo undefined (en modo estricto) o el objeto global.
En cambio, una arrow function conserva el this del método que la contiene (lo que se conoce como this léxico):
Dentro de la arrow function, this sigue siendo counter, porque las funciones flecha no crean su propio this. Antes de que existieran, la gente resolvía esto con trucos como const self = this; o .bind(this). Esos patrones siguen funcionando, pero hoy casi nunca hacen falta.
Qué NO tienen las arrow functions
La regla del this léxico forma parte de un patrón más amplio: las funciones flecha se saltan varias cosas que sí tienen las funciones tradicionales.
- No tienen su propio
this— lo heredan del scope que las envuelve. - No tienen objeto
arguments— en su lugar, usa rest parameters (...args). - No admiten
new— no puedes usar una arrow function como constructor. - No tienen propiedad
prototype— consecuencia directa de no poder usarse connew.
...args te da la misma capacidad de "aceptar cualquier cantidad de argumentos" que tenía arguments, con la ventaja de que es un array de verdad. La llamada new Greeter(...) falla porque las funciones flecha no son constructoras.
Cuándo no usar una arrow function
Las funciones flecha son la opción por defecto para los callbacks, pero hay situaciones en las que son la elección equivocada:
Los métodos de objeto definidos con sintaxis de flecha no enlazan this al objeto: lo heredan del lugar donde se escribió el literal del objeto (normalmente el módulo o el scope global). Para los métodos, usa mejor la forma abreviada (greet() { ... }).
Lo mismo aplica a los métodos de prototipo de una clase, a los manejadores de eventos que necesitan que this apunte al elemento, y a cualquier función que pienses invocar con new. Ahí la herramienta correcta es una function tradicional.
Regla rápida para decidir
- ¿Un callback corto, de una sola expresión? Arrow function.
- ¿Necesitas que
thissiga apuntando al scope exterior? Arrow function. - ¿Estás definiendo un método en un objeto o en el prototipo de una clase? Función tradicional (o método abreviado).
- ¿Estás escribiendo un constructor? Función tradicional, o mejor aún, una
class.
En cualquier proyecto real de JavaScript vas a ver los dos estilos mezclados. Distinguir cuál encaja mejor es cuestión de leer código ajeno durante unas semanas; a partir de ahí, la elección sale sola.
Lo que viene: parámetros y valores por defecto
Las arrow functions y las funciones tradicionales comparten exactamente las mismas características en los parámetros: valores por defecto, parámetros rest, desestructuración de argumentos y más. Eso lo vemos en la siguiente página, y vale para todas las formas de función que hemos visto hasta ahora.
Preguntas frecuentes
¿Qué es una arrow function en JavaScript?
Una arrow function (o función flecha) es una forma más corta de escribir una función usando la sintaxis =>. Por ejemplo, const add = (a, b) => a + b; define una función flecha que recibe dos argumentos y devuelve su suma. Además de ser más breve, las arrow functions no tienen su propio this, ni arguments, ni super: los heredan del ámbito donde están definidas.
¿En qué se diferencian las arrow functions de las funciones normales?
Las funciones declaradas con function tienen su propio this, su propio objeto arguments y se pueden usar como constructores con new. Las arrow functions no tienen nada de eso: heredan el this del contexto donde se definen. Tampoco se hacen hoisting como sí ocurre con las declaraciones de función.
¿Cuándo conviene usar una arrow function?
Son ideales para callbacks cortos (arr.map(x => x * 2)) y para cualquier situación en la que quieras que this siga apuntando al contexto exterior, por ejemplo cuando dentro de un método de clase pasas un handler a setTimeout o a un listener de eventos. Para métodos de objetos, constructores y funciones sueltas a nivel superior, es mejor quedarse con function.