Un modo più breve di scrivere una funzione
Le arrow function sono la sintassi più compatta di JavaScript per definire una funzione. Sono arrivate con ES2015 e oggi sono lo stile predefinito per le funzioni piccole e inline, soprattutto le callback passate ai metodi degli array, gli handler delle promise e gli event listener.
La forma è: parametri, poi =>, poi il corpo.
Stesso risultato della forma classica:
const add = function (a, b) {
return a + b;
};
Scrivere meno è comodo, ma i veri motivi per interessarsi alle arrow function sono il return implicito e il modo in cui trattano this. Li vediamo tra poco.
Return implicito
Se il corpo è una singola espressione, puoi togliere le graffe e il return. Il valore dell'espressione viene restituito automaticamente:
È qui che le arrow function danno il meglio: le trasformazioni di una riga si leggono come formule matematiche:
Appena ti serve più di un'istruzione, tornano le graffe e un return esplicito:
Dimenticare il return dentro le graffe è l'errore più comune con le arrow function. x => { x * 2 } restituisce undefined: le graffe trasformano il corpo in un blocco e l'espressione al suo interno viene scartata.
Parentesi dei parametri
Con un solo parametro, le parentesi sono facoltative:
Con zero, due o più parametri, sono obbligatorie:
Alcuni team preferiscono usare sempre le parentesi per coerenza. Vanno bene entrambi gli stili: scegline uno e mantienilo.
Restituire un oggetto letterale
Ecco un tranello. Se provi a restituire implicitamente un oggetto, le graffe sembrano il corpo di una funzione:
Questo stampa undefined. JavaScript legge { name: name } come un blocco con un'istruzione etichettata, non come un oggetto letterale. Racchiudi l'oggetto tra parentesi per forzarlo a essere un'espressione:
Le () attorno a { ... } sono la soluzione. Tienilo a mente: ci sbatterai contro entro la prima settimana di arrow function.
this lessicale
Il vero motivo per cui esistono le arrow function non è la sintassi più breve. È che non hanno un proprio this: lo ereditano dallo scope circostante.
Per capire perché conta, guarda una funzione normale usata come callback:
Dentro la function () { ... } passata a setInterval, this non è counter. Le funzioni normali ricevono un proprio this in base a come vengono chiamate, e setInterval chiama la callback con this uguale a undefined (in strict mode) oppure all'oggetto globale.
Una arrow function mantiene il this del metodo che la contiene:
Dentro la arrow function, this è ancora counter perché la freccia non introduce un proprio this. Prima delle arrow function si aggirava il problema con const self = this; o .bind(this). Questi pattern funzionano ancora, ma oggi servono di rado.
Cosa non hanno le arrow function
La regola del this lessicale fa parte di uno schema più ampio: le arrow function rinunciano a diverse cose che le funzioni normali hanno.
- Nessun
thisproprio: viene ereditato dallo scope esterno. - Nessun oggetto
arguments: usa invece i parametri rest (...args). - Niente
new: non puoi usare una arrow function come costruttore. - Nessuna proprietà
prototype: è una conseguenza della regola sulnew.
...args ti dà la stessa possibilità di "accettare un numero qualsiasi di argomenti" che dava arguments, con il vantaggio che è un vero array. La chiamata new Greeter(...) genera un errore perché le arrow function non sono costruttori.
Quando non usare una arrow function
Le arrow function sono la scelta predefinita per le callback, ma in alcuni casi sono la scelta sbagliata:
I metodi di un oggetto definiti con la sintassi a freccia non hanno this legato all'oggetto: lo ereditano dal punto in cui è stato scritto l'oggetto letterale (di solito lo scope del modulo o quello globale). Per i metodi usa la sintassi abbreviata (greet() { ... }).
Lo stesso vale per i metodi del prototype di una classe, per i metodi usati come event handler che hanno bisogno che this sia l'elemento, e per qualsiasi funzione che vuoi chiamare con new. Lì lo strumento giusto è una function normale.
Una regola pratica veloce
- Callback breve, una sola espressione? Arrow function.
- Ti serve che
thisresti legato allo scope esterno? Arrow function. - Stai definendo un metodo su un oggetto o sul prototype di una classe? Funzione normale (o metodo abbreviato).
- Stai scrivendo un costruttore? Funzione normale, o meglio ancora una
class.
In qualsiasi codebase JavaScript reale vedrai entrambi gli stili. Per capire quale si adatta meglio bastano poche settimane passate a leggere il codice degli altri: dopo, la scelta diventa automatica.
Prossimo passo: parametri e valori predefiniti
Arrow function e funzioni normali condividono le stesse funzionalità per i parametri: valori predefiniti, parametri rest, argomenti destrutturati e altro. È l'argomento della prossima pagina, e vale per tutte le forme di funzione viste finora.
Domande frequenti
Cos'è una arrow function in JavaScript?
Una arrow function (o funzione freccia) è un modo più breve di scrivere una funzione, con la sintassi =>. const add = (a, b) => a + b; è una arrow function che riceve due argomenti e ne restituisce la somma. Oltre alla sintassi più compatta, le arrow function non hanno un proprio this, né arguments, né super: li ereditano dallo scope circostante.
Che differenza c'è tra arrow function e funzioni normali?
Le funzioni dichiarate con function hanno un proprio this, un proprio oggetto arguments e possono essere usate come costruttori con new. Le arrow function non hanno niente di tutto questo: ereditano this dal punto in cui sono definite. Inoltre le arrow function non subiscono l'hoisting come le dichiarazioni di funzione.
Quando conviene usare una arrow function?
Usale per le callback brevi (arr.map(x => x * 2)) e ovunque tu voglia che this resti legato allo scope esterno, per esempio dentro un metodo di classe che passa un handler a setTimeout o a un event listener. Usa una function normale per i metodi degli oggetti, i costruttori e le funzioni autonome di primo livello.