back to top

ES6+: Arrow functions vs funzioni classiche

Introduzione: Cos’è ES6 e Perché le Arrow Functions Sono Così Importanti

ECMAScript 2015, più comunemente chiamato ES6, è una delle versioni più rivoluzionarie di JavaScript, il linguaggio che alimenta gran parte del web moderno. Con ES6, introdotto ufficialmente nel 2015, gli sviluppatori hanno ricevuto un ricco pacchetto di nuove funzionalità che hanno reso il codice più pulito, leggibile e potente.

Tra le novità più rilevanti troviamo:

  • let e const per la dichiarazione di variabili.
  • Template string per la creazione di stringhe dinamiche.
  • Classi per un approccio di programmazione orientato agli oggetti più vicino a linguaggi come Java o C#.
  • Moduli import/export per organizzare il codice in file separati.
  • Arrow functions, oggetto centrale di questo articolo, che hanno semplificato notevolmente la definizione di funzioni, soprattutto nelle callback.

Le arrow functions non sono un semplice “abbellimento” sintattico, ma uno strumento efficace per rendere il codice più conciso e, in determinate circostanze, più semplice da manutenere. La caratteristica distintiva che le rende così utili è la gestione del contesto (this) e l’ereditarietà del this lessicale. In questo articolo analizzeremo nel dettaglio tutto ciò che ti serve sapere sulle arrow functions: dalla sintassi ai vantaggi, dalle limitazioni ai casi d’uso, con esempi pratici e consigli su quando preferirle alle funzioni classiche.

Se sei totalmente alle prime armi con JavaScript, potrebbe esserti utile dare un’occhiata anche alla Guida Completa a JavaScript per Principianti, dove trovi spiegazioni approfondite sugli elementi di base del linguaggio. Se invece hai già una certa dimestichezza e vuoi scoprire le funzionalità emergenti, potresti consultare Novità JavaScript ES2025 per avere un quadro sulle evoluzioni più recenti e sui prossimi passi del linguaggio.

Iniziamo la nostra trattazione partendo dalle differenze principali tra arrow functions e funzioni classiche.


Differenze Principali tra Arrow Functions e Funzioni Classiche

Le arrow functions rappresentano un’innovazione su più fronti. Oltre a una sintassi più compatta, offrono un comportamento peculiare nella gestione di variabili come this, arguments, super. Per comprendere quando conviene usarle, dobbiamo prima delineare con chiarezza in cosa differiscono dalle funzioni tradizionali.

1. Sintassi e Struttura

La differenza più evidente risiede nella sintassi.

  • Funzione classica: function saluta(nome) { return `Ciao, ${nome}!`; } console.log(saluta("Mario")); // Output: Ciao, Mario!
  • Arrow function (versione con blocco e return esplicito): const saluta = (nome) => { return `Ciao, ${nome}!`; }; console.log(saluta("Mario")); // Output: Ciao, Mario!
  • Arrow function (versione compatta, single-line): const saluta = (nome) => `Ciao, ${nome}!`; console.log(saluta("Mario")); // Output: Ciao, Mario!

La sintassi più breve è uno dei vantaggi principali, specialmente quando devi definire funzioni anonime in forma di callback all’interno di altre funzioni, come map(), filter() o reduce().

Inoltre, se la funzione ha un solo parametro, puoi omettere anche le parentesi tonde:

const saluta = nome => `Ciao, ${nome}!`;

2. Gestione del this (Contesto Lessicale)

Le arrow functions ereditano il this dal contesto esterno in cui sono definite. Questo si traduce in un comportamento molto diverso rispetto alle funzioni classiche, nelle quali this può cambiare in base a come la funzione viene invocata (ad esempio, come metodo di un oggetto o come funzione stand-alone).

Esempio di problema con le funzioni classiche:

function Persona(nome) {
  this.nome = nome;
  // Funzione classica annidata
  setTimeout(function () {
    console.log(this.nome); // `this` non punta più all'istanza Persona in modalità strict
  }, 1000);
}

new Persona("Alice");

In modalità strict ("use strict"), this in una funzione classica chiamata in modo indipendente è undefined. In ambienti non strict, this punterebbe invece all’oggetto globale (ad esempio window in un browser). Entrambi i comportamenti sono indesiderati in questo contesto.

La soluzione spesso era salvare il riferimento all’istanza in una variabile:

function Persona(nome) {
  this.nome = nome;
  const self = this;
  setTimeout(function () {
    console.log(self.nome); // Alice
  }, 1000);
}

Con le arrow functions, non occorre fare questo “trick”, perché il this è “catturato” lessicalmente dalla funzione esterna:

function Persona(nome) {
  this.nome = nome;
  setTimeout(() => {
    console.log(this.nome); // Alice
  }, 1000);
}

In tal modo, this continua a riferirsi all’istanza corrente di Persona.

3. Assenza di arguments e Uso dello Spread Operator

Le arrow functions non hanno l’oggetto arguments. Se hai bisogno di accedere a tutti gli argomenti passati a una funzione senza dichiararli esplicitamente, con le funzioni classiche puoi farlo tramite arguments, mentre con le arrow functions dovrai usare lo spread operator:

const sommaTutti = (...numeri) => numeri.reduce((acc, val) => acc + val, 0);

Nelle funzioni classiche, potevi usare:

function sommaTutti() {
  let total = 0;
  for (const numero of arguments) {
    total += numero;
  }
  return total;
}

4. Comportamento nei Metodi di Oggetti

Se definisci un metodo di un oggetto come arrow function, devi ricordare che this punterà al contesto lessicale, non all’oggetto stesso. Questo può causare errori inaspettati:

const robot = {
  nome: "C3PO",
  saluta: () => {
    console.log(`Salve, sono ${this.nome}`);
  },
};

robot.saluta(); // undefined, non 'C3PO'

Per i metodi di oggetti, le funzioni classiche rimangono spesso l’opzione migliore:

const robot = {
  nome: "C3PO",
  saluta() {
    console.log(`Salve, sono ${this.nome}`);
  },
};

robot.saluta(); // Salve, sono C3PO

5. Uso negli Event Handler

In contesti come la manipolazione del DOM, se scrivi ad esempio:

button.addEventListener("click", function () {
  console.log(this); // L'elemento button
});

this fa riferimento all’elemento button. Se usi invece una arrow function:

button.addEventListener("click", () => {
  console.log(this); // `this` è ereditato dal contesto esterno
});

Potrebbe puntare a un oggetto completamente diverso (o undefined in strict mode), a seconda di dove è definita la arrow function. Quindi, se vuoi sfruttare this per fare riferimento all’elemento sul quale è stato agganciato l’evento, le funzioni classiche sono più indicate.


Vantaggi delle Arrow Functions

  1. Sintassi Concisa:
    La forma ridotta rende il codice più agile, specialmente nelle callback o in funzioni di ordine superiore come map, filter, reduce.
  2. Gestione del this semplificata:
    Non serve più salvare this in variabili come self o that. Questo risolve molte situazioni in cui, con le funzioni classiche, ci si confonde a causa del cambio di contesto.
  3. Maggiore Leggibilità per Callback Brevi:
    Spesso, gli sviluppatori apprezzano la sintassi a freccia quando il corpo della funzione è molto semplice, ad esempio una singola operazione: array.map(x => x * 2);
  4. Perfette per Funzioni Anonime:
    Le arrow functions sono state pensate proprio per coprire l’uso di funzioni anonime, molto comuni in JavaScript (ad esempio, passate a un gestore di eventi, a un timer, ecc.).

Se vuoi spingere ulteriormente l’uso avanzato di JavaScript e scoprire altre tecniche moderne, dai un’occhiata a JavaScript: le tecniche avanzate per dev, dove trovi diversi spunti interessanti sulla scrittura di codice efficiente e avanzato.


Limitazioni e Casi in cui le Funzioni Classiche Rimangono Preferibili

Le arrow functions non sono una soluzione universale per tutte le necessità di JavaScript. Ecco alcune situazioni in cui potresti voler continuare a usare le funzioni tradizionali.

  1. Metodi negli Oggetti:
    Se vuoi mantenere il this che fa riferimento all’oggetto stesso, come visto negli esempi di robot e persona, le funzioni classiche (o la “method shorthand” di ES6) sono più indicate.
  2. Costruttori:
    Non è possibile usare new su una arrow function. Se stai definendo un costruttore di oggetti o classi (anche se oggi si preferiscono le classi ES6), devi usare una funzione classica.
  3. arguments e Overload:
    Le arrow functions non dispongono dell’oggetto arguments. Se vuoi un numero variabile di argomenti senza definire parametri specifici o lo spread operator, dovrai continuare a usare le funzioni classiche.
  4. Event Handler che Necessitano del this del DOM Element:
    Come già detto, in certi casi (soprattutto quando devi manipolare il DOM) può essere preferibile la funzione classica per avere il this riferito all’elemento su cui è stato agganciato l’evento.
  5. Confusione tra Sintassi Metodi e Arrow Functions:
    In un oggetto letterale, potresti preferire la sintassi: const utente = { nome: "Marco", saluta() { console.log(`Ciao, sono ${this.nome}`); }, }; invece di const utente = { nome: "Marco", saluta: () => { console.log(`Ciao, sono ${this.nome}`); }, }; per evitare il già citato problema del this ereditato.

Esempi Pratici: Trasformazione ed Errori Comuni

La comprensione teorica è essenziale, ma niente come qualche esempio pratico può aiutare a interiorizzare i concetti. Vediamo alcuni casi utili a fissare le idee.

1. Da Funzione Classica ad Arrow Function

Funzione classica:

function moltiplica(a, b) {
  return a * b;
}

console.log(moltiplica(3, 5)); // 15

Arrow function (versione con blocco):

const moltiplica = (a, b) => {
  return a * b;
};

console.log(moltiplica(3, 5)); // 15

Arrow function (versione compatta):

const moltiplica = (a, b) => a * b;

Osserva come, quando il corpo della funzione è costituito da una sola riga che restituisce un valore, puoi omettere sia le parentesi graffe sia la parola chiave return.

2. Restituire un Oggetto in una Arrow Function (Parentesi Tonde)

Un errore comune si verifica quando vuoi restituire un oggetto letterale in una arrow function monoriga. Se scrivi:

const creaStudente = (nome, eta) => { nome: nome, eta: eta };

JavaScript interpreterà le parentesi graffe dopo la freccia come l’inizio di un blocco e non di un oggetto. Pertanto, la funzione restituirà undefined. Per ovviare a ciò, devi avvolgere l’oggetto tra parentesi tonde:

const creaStudente = (nome, eta) => ({ nome: nome, eta: eta });

3. Impossibilità di Usare new

Se provi a fare:

const Persona = (nome) => {
  this.nome = nome;
};

const mario = new Persona("Mario"); // Errore

Ottieni un errore, perché le arrow functions non possono essere usate come costruttori. In questi casi, dovresti:

function Persona(nome) {
  this.nome = nome;
}

const mario = new Persona("Mario");

Oppure ricorrere alle classi (che internamente funzionano comunque come funzioni costruttore):

class Persona {
  constructor(nome) {
    this.nome = nome;
  }
}

const mario = new Persona("Mario");

4. Mancanza di arguments

Le arrow functions non hanno arguments, perciò:

const sommaDinamica = () => {
  console.log(arguments); // ReferenceError: arguments is not defined
};

Se desideri passare un numero indefinito di parametri a una arrow function, utilizza lo spread operator:

const sommaDinamica = (...numeri) => {
  return numeri.reduce((acc, val) => acc + val, 0);
};

Best Practice: Quando Usare Arrow e Quando Preferire le Funzioni Classiche

Dopo aver coperto vantaggi e svantaggi, diamo una serie di linee guida più generali:

  1. Callback in Metodi come map, filter, reduce
    • Arrow: Ideali. Lo scopo è scrivere funzioni brevi, spesso monoriga. jconst numeri = [1, 2, 3, 4]; const doppi = numeri.map(num => num * 2);
  2. Funzioni Costruttrici e Classi
    • Funzioni Classiche o Classi ES6: Necessarie, dato che le arrow functions non sono istanziabili con new.
  3. Eventi DOM
    • Funzioni Classiche: Se hai bisogno di this collegato all’elemento su cui è impostato l’evento.
    • Arrow: Se non ti serve fare riferimento all’elemento e vuoi richiamare oggetti esterni.
  4. Metodi Oggetto
    • Funzioni Classiche: Garantiscono che this punti all’oggetto.
    • Arrow: Spesso da evitare in questo contesto, a meno che tu non voglia esplicitamente “perdere” il this dell’oggetto.
  5. Performance e Code Style
    • In genere, la scelta dell’una o dell’altra non ha un impatto enorme sulle performance reali del tuo codice (a meno di casi estremi), ma sulla manutenibilità e leggibilità. In un progetto di gruppo, conviene stabilire linee guida su quando e come usare le arrow functions.

Per scoprire altre tecniche e strategie di sviluppo JavaScript moderno, puoi leggere JavaScript: le tecniche avanzate per dev. Lì troverai ulteriori spunti su come gestire al meglio le peculiarità del linguaggio, inclusi pattern architetturali e ottimizzazioni di performance.


Panoramica su Altre Feature Introdotte con ES6 (Contesto)

Le arrow functions non sono l’unica grande novità. Per avere un quadro più ampio sulle potenzialità di JavaScript, e per vedere come il linguaggio si è evoluto ulteriormente in questi anni, ti consiglio di dare un’occhiata anche alle functionalità ES6 e oltre (ES7, ES8, …), fino ai più recenti standard. Ad esempio, con ES6 e successivi abbiamo:

  • Parametri di default: function saluta(nome = "Ospite") { console.log(`Ciao, ${nome}!`); }
  • Rest Operator e Spread Operator: const array1 = [1, 2]; const array2 = [3, 4]; const unione = [...array1, ...array2]; // [1, 2, 3, 4]
  • Destructuring: const persona = { nome: "Luca", eta: 25 }; const { nome, eta } = persona;
  • Classi: class Utente { constructor(nome) { this.nome = nome; } saluta() { console.log(`Ciao, ${this.nome}!`); } }

e molto altro. Se vuoi una panoramica sulle funzionalità che stanno arrivando (o sono già arrivate) nelle versioni più recenti e future di JavaScript, non dimenticare di consultare Novità JavaScript ES2025.


Approfondimenti Esterni (MDN, JavaScript Info)

Per consultare documentazioni ufficiali e guide dettagliate, considera alcune fonti autorevoli:


Conclusioni e Suggerimenti Finali per un Uso Efficace

Le arrow functions fanno ormai parte dell’equipaggiamento standard di ogni sviluppatore JavaScript moderno. Saperle utilizzare correttamente ti permette di scrivere codice più conciso e, in certe situazioni, più lineare. Tuttavia, non devi dimenticare i casi in cui le funzioni classiche restano una scelta migliore. Riassumiamo i principi chiave:

  1. Abitudini di Sintassi
    • Usa la versione compatta delle arrow functions quando il corpo della funzione è molto breve e fornisce un immediato return.
    • Evita di complicare eccessivamente la logica all’interno di una arrow function monoriga: se diventa troppo lunga o complessa, adotta una sintassi più esplicita.
  2. Consapevolezza di this
    • Le arrow functions ereditano il this dal contesto lessicale; ottimo per callback in metodi asincroni, ma potenzialmente problematico se devi riferirti all’oggetto correntemente in uso.
  3. Callback e Funzioni Anonime
    • Perfette per map, filter, reduce o per la definizione di piccole funzioni inline. Alleggeriscono visivamente il codice e migliorano la leggibilità.
  4. Costruttori e Metodi Oggetti
    • Non utilizzare arrow functions come costruttori: non funzionano.
    • Nei metodi di un oggetto, preferisci le funzioni classiche o la sintassi ES6 di method shorthand per conservare il this dell’oggetto.
  5. Team e Coerenza
    • Se lavori in team, convieni delle regole condivise. Le arrow functions possono migliorare la qualità del codice se tutti le usano in maniera coerente e consapevole.

Seguendo queste linee guida, potrai sfruttare al massimo i vantaggi introdotti da ES6 e mantenerli ordinati in un codice che risulti comprensibile per te e i tuoi colleghi. Se desideri continuare ad approfondire il mondo JavaScript, puoi consultare gli articoli interni già suggeriti, così come altre risorse avanzate.

Buon coding e buona sperimentazione con le arrow functions!

Condividi

Articoli Recenti

Categorie popolari