back to top

Javascript Arrow Function: le funzioni a freccia di ES6

In questo articolo vedremo cosa sono e come si utilizzano le arrow function, una delle novità più significative introdotte nel linguaggio Javascript dalla specifica da ECMAScript 6.

Cosa sono le arrow function?

Le "arrow function" (che vengono comunemente chiamate anche "fat arrow functions" o, in italiano, funzioni a freccia) sono sostanzialmente una modalità alternativa con cui scrivere le funzioni all’interno del codice Javascript moderno. Lo sviluppatore moderno non cerca infatti solamente di produrre codice tecnicamente e qualitativamente performante, ma ambisce anche a ridurre la quantità fisica di codice necessaria alla produzione di una determinata task, per produrre applicazioni sempre più DRY (Don’t Repeat Yourself) e sempre meno prolisse/verbose. Scrivere codice più conciso, con uno scope semplificato, e risparmiare tempo in fase di scrittura sono infatti le due caratteristiche principali offerte da questa nuova funzionalità.

Per una definizione immediata, possiamo dire che le arrow functions sono funzioni concise, prodotte tramite una linea di codice (one-line functions) molto simili alle "funzioni lambda" di linguaggi quali C# e Python. Per produrre le arrow functions nella loro versione più compatta infatti, non occorre dichiarare ne la keyword "function", ne la keyword "return" (che risulta essere implicita) ne tantomeno le parentesi graffe che racchiudono il corpo delle funzioni "{" e "}".

Arrow functions: come funzionano

Senza utilizzare la sintassi delle arrow functions, se volessivo dichiarare una funzione in Javscript, scriveremmo qualcosa di simile al seguente snippet di codice:

function moltiplica(params) {
  return params * 2;
}

moltiplica(4);  // 8

La funzione custom "moltiplica" accetta un parametro numerico in ingresso che andrà a moltiplicare per 2 all’interno del suo corpo e restituirà infine allo stream di codice principale. Se quindi passiamo il valore 4, la funzione restituirà 8. Vediamo come possiamo scrivere l’equivalente codice utilizzando la notazione delle arrow functions:

var moltiplica = (params) => params * 2;

moltiplica(4);  // 8

Come possiamo notare, le arrow functions ricavano il proprio nome dalla presenza principale del simbolo ("=>") che ricorda la forma di un arco con una freccia e che rapprensenta in Javascript la modalità con cui queste funzioni vengono definite.

Questi due approcci di scrittura specifici producono lo stesso risultato. Con l’avvento delle arrow functions ora è sempre più raro trovare la keyword "function", nei casi in cui una funzione debba operare sui parametri di ingresso e restituire un valore.

Come possiamo vedere, non abbiamo occorrenza di digitare le keywords comunemente utilizzate per definire le funzioni (function, return e { }), ed utilizziamo l’operatore arrow "=>" come "separatore" di firma e corpo della funzione. La sintassi per questo tipo di arrow function è dunque la seguente:

(parametro) => espressione

Inoltre, se, come nel caso precedente, la arrow function accetta un solo parametro nella sua firma, le parentesi tonde "(" e ")" diventano puramente opzionali. Possiamo dunque scrivere:

parametro => espressione

che, nella pratica, si traduce in:

var moltiplica = params => params * 2;

moltiplica(4);  // 8

per una sintassi ancora più concisa!

Se invece non abbiamo nessun parametro, è consigliato utilizzare le parentesi in fase di definizione della firma della funzione, scrivendo:

() => espressione

che si traduce in, ad esempio:

var stampaX = () => 42;

stampaX(); // 42

Infine, se abbiamo una moltitudine di parametri, dovremo obbligatoriamente includere le parentesi tonde, scrivendo:

(parametro1, parametro2, ..., parametroN) => espressione

che nella pratica potremmo scrivere nel seguente modo:

var moltiplicaXY = (x, y) => x * y;

moltiplicaXY(10, 20); // 200

Nota: non è ammesso l’utilizzo di interruzioni di riga tra la firma della funzione arrow (definita tra parentesi tonde) e il suo arco "=>", previa la produzione di un errore.

Espressioni vs statements

In tutti questi esempi che abbiamo utilizzato, non abbiamo avuto la necessità di includere le parentesi graffe per avviluppare il corpo delle arrow functions, perchè ci siamo avvalsi delle "espressioni" e non dei cosiddetti "statements". Per una definizione immediata, possiamo dire che gli statements producono un azione, mentre le espressioni producono un valore.

La seguente è una regola fondamentale da ricordare in fase di costruzione di una arrow function: se utilizziamo un’espressione all’interno del corpo della funzione, non abbiamo occorrenza di avvilupparlo nelle parentesi graffe, contrariamente, se utilizziamo statements, le parentesi graffe diventano obbligatorie. Non è tutto: una volta che abbiamo la presenza delle parentesi graffe, la keyword "return" non sarà più implicita, e dovremo necessariamente includerla per resituire il valore elaborato all’interno della funzione, altrimenti questa resituirà il valore "undefined". Vediamo un esempio.

Il seguente codice restituisce normalmente il valore rappresentato dalla moltiplicazione, ad esempio, passando i valori 10 e 20, otteremo 200:

var moltiplicaXY = (x, y) => x * y;

Il seguente codice produce il valore "undefined", dato che abbiamo la presenza delle parentesi graffe ma non la keyword "return" dichiarata esplicitamente:

var moltiplicaXY = (x, y) => { x * y};

Infine, nel seguente codice, viene prodotto correttamente il valore della moltiplicazione, dato che sono presenti sia le parentesi graffe, sia la keword "return":

var moltiplicaXY = (x, y) => { return x * y};

Vediamo ora un esempio di una arrow function in cui al posto di un’espressione, viene utilizzato uno statement (azione):

var checkColor = (color) => {
  if (color === 'red') {
    return 'il colore è rosso';
  } else {
    return 'il colore non è rosso';
  }
}

La funzione "checkColor" è una arrow function più corposa, perchè deve necessariamente includere le parentesi graffe e la clausola "return" disposta in base alla composizione della struttura condizionale. Potremo dunque utilizzarla come segue:

checkColor('red');

che restituirà il letterale stringa avente valore "il colore è rosso", e:

checkColor('blue');

che invece produrrà il letterale stringa avente valore "il colore non è rosso".

Restituire un letterale oggetto

Nel caso in cui abbiamo l’occorrenza di restituire come valore dell’arrow function un letterale oggetto, dobbiamo avvilupparlo obbligatoriamente in parentesi tonde, per non indurre in errore il motore Javascript e scambiarlo per il corpo della funzione stessa. Vediamo un esempio. Vogliamo creare una arrow function che restituisce un letterale oggetto avente come proprietà denominata "color" un valore stringa passato come parametro. Scriveremo dunque:

var getObj = (x) => ( {'color': x} );

console.log( getObj('red') );  // {'color': 'red'}

In alternativa, avremmo potuto essere più espliciti utilizzando la keyword "return", ma in questo caso non avremmo avuto la necessità di includere le parentesi tonde:

var getObj = (x) => { return {'color': x}};

Non possiamo però scrivere la arrow function senza le suddette parentesi tonde:

var getObj = (x) => {'color': x};

altrimenti il motore Javascript produrrà giustamente l’errore "Uncaught SyntaxError: Unexpected token :".

Le arrow functions sono anonime

E’ importante sottolineare che le arrow functions sono funzioni "anonime", ovvero non possiedono un nome esplicito. Questa caratteristica comporta un paio di problematiche di cui è fondamentale essere consci.

La prima: le arrow functions sono più difficili da analizzare in fase di debug. Quando il motore Javascript produce un errore, non potremo infatti tenere traccia del nome della funzione che produce l’errore. La seconda: non avendo nome esplicito, le arrow function non hanno un auto-riferimento. Questo signigica che se dobbiamo usare una arrow function all’interno di particolari contesti, come la ricorsione, o un gestore degli eventi che necessita di essere "slegato" (o "sganciato") da una funzione handler, il codice non funzionerà.

Un utilizzo particolare della keyword "this"

Il valore identificato dalla keyword "this", nelle espressioni di funzioni scritte attraverso la sintassi comune, è legato al contesto in cui viene richiamata la funzione stessa, e può essere agganciato a particolari oggetti piuttosto che ad altri. Tuttavia, con le arrow functions, "this" è agganciato "lessicalmente" (lexical binding). Questo significa che viene utilizzato "this" dal contesto del codice che contiene la arrow function. Ad esempio, possiamo utilizzare lo scope globale:

var color = 'red';
var arrow = () => {
   return this.color;
};

arrow(); // 'red'

Oppure, nel seguente contesto:

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| si riferisce all'oggetto Person
  }, 1000);
}

var p = new Person();

il valore di "this" che viene passato alla funzione passata alla funzione built-in "setInterval" possiede lo stesso valore del "this" nel contesto del codice che la contiene. Ne consegue che in fase di ricerca del "this" non presente nello scope corrente, viene prodotto il "this" dello scope contenitore.

Questa caratteristica si rivela estremamente utile nel caso in cui le arrow functions vengono definite in contesti innestati, dove a volte diventa difficoltoso tenere traccia dei valori di "this" e dove la strategia migliore precedente alla specifica ECMAScript 6 consisteva nell’assegnazione di "this" ad una variabile (chiamata chiusura), di solito scritta nel seguente modo: var self = this;. Inoltre, occorre tenere ben presenti due caratteristiche molto importanti a questo riguardo. Dato che le arrow functions non possiedono un loro "this", le chiamate ai metodi built-in "call" e "apply" possono essere effettuate solo con parametri formali, mentre "thisArg" verrà ignorato. Le arrow functions infine, a differenza delle funzioni comuni, non possiedono un loro oggetto "arguments" (che identifica i parametri passati in ingesso alla funzione). In questo contesto "arguments" è solo un riferimento all’oggetto "arguments" dello scope contenitore, come nel caso di "this".

Since arrow functions do not have their own this, the methods call() or apply() can only pass in parameters. thisArg is ignored.

Quando non utilizzare le arrow functions

A causa delle sue caratteristiche intrinseche, ci sono dei casi in cui è inopportuno utilizzare le arrow functions al posto delle funzioni comuni. Vediamoli.

1) Se peggiora la leggibilità del codice

Se è vero che con l’introduzione delle arrow functions gli sviluppatori sono andati in visibilio facendole diventare la funzionalità più utilizzata tra quelle offerte dalle nuove specifiche, è anche vero che non bisogna abusare della suddetta sintassi solo perchè una caratterista moderna. Essendo anonime, le arrow functions possono peggiorare sensibilmente la leggibilità del codice, oppure non essere di immediata comprensione per sviluppatori abituati ai precedenti standard. In questi casi , una migrazione più "graduale" risulta essere la scelta vincente.

2) All’interno dei metodi degli oggetti

Dato che, come abbiamo appena visto, il valore di "this" all’interno delle arrow functions non è agganciato a nulla (una delle maggiori differenze tra arrow functions e funzioni comuni), non possiamo utilizzarlo in modo coerente all’interno dei metodi di un oggetto. Verrà invece erditato il valore dallo scope parente, come abbiamo visto poc’anzi.

var skills = {
  stamina: 9,
  decreaseStamina: () => {
    this.stamina--;
  }
}

In questo esempio non potremo accedere al valore "this.stamina" come nel caso di un metodo dichiarato con una funzione classica.

3) Come funzione "callback" con contesto dinamico

Sempre a causa della mancanza di aggancio a "this", le funzioni arrow non sono la scelta più indicata nei contesti dinamici, come ad esempio gli event listener.

var element = document.getElementById('myEl');
element.addEventListener('click', () => {
  this.addClass('newClass');
});

In questo caso otteremo un errore di tipo "TypeError", perchè "this" non è agganciato all’oggetto Element prelevato dal DOM grazie al metodo "getElementById", ma piuttosto è legato allo scope parente.

4) Come funzioni costruttori

Le arrow functions non possono essere utilizzate in combinazione con l’operatore "new". Ne consegue che non possiamo utilizzare le arrow functions come costruttori, utili alla produzione di oggetti/istanze di una particolare classe. Anche il seguente codice produrrà infatti un errore di tipo "TypeError".

var MYClass = () => {};  
var mc = new MYClass();

5) In combinazione con la proprietà "prototype"

Le arrow functions non possiedono una proprietà "prototype" come le funzioni comuni. Pertanto, la chiamata alla suddetta proprietà produrrà il valore "undefined".

Quando utilizzare le arrow functions

Contrariamente, ci sono dei casi specifici in cui l’utilizzo delle arrow functions è ottimale. Dato che le suddette funzioni sono controparti delle cosiddette "lambda functions", possono essere applicate egregiamente nei contesti in cui vengono utilizzate una moltitudine di volte, come ad esempio negli apprezzatissimi metodi "map", "reduce", "filter" e "forEach".

Il metodo "map" permette di produrre un nuovo array, basato su un array precedente, in cui i valori vengono calcolati dinamicamente ad ogni iterazione del ciclo sul suddetto array. Ad esempio, nel seguente caso:

var old_array = ['a', 'b', 'c'];
var new_array = old_array.map(function(el) {
  return 'ciao ' + el;
});

console.log(new_array);

nella variabile denominata "new_array" verranno salvati i valori "ciao a", "ciao b", "ciao c", perchè la variabile "old_array" viene "mappata" e utilizzata nel contesto del ciclo applicato.

Grazie alle arrow functions possiamo ridurre la quantità di codice in modo considerevole scrivendo.

var old_array = ['a', 'b', 'c'];
var new_array = old_array.map((el) => 'ciao ' + el);

console.log(new_array);

Grazie al moderno metodo "forEach" (che in molti casi soppianta il vecchio ciclo "for") e grazie al fatto che le arrow functions mantengono il "this" del contesto parente, possiamo creare cicli fortemente intuitivi, come ad esempio:

this.myObj.forEach(param => {    
  this.myMethod(param);  
});

Conclusioni

Come abbiamo potuto constatare, le arrow functions, come tutte le nuove funzionalità offerte da ECMAScript 6 portano con se enormi cambiamenti nella scrittura di codice Javascript. Sebbene in alcuni casi le arrow functions possono essere considerate degli "shortcut anonimi" delle funzioni comuni, è un errore considerarle solamente in questo senso. Le arrow functions non costituiscono infatti solo una sintassi alternativa, ma comportano tutta una serie di caratteristiche specifiche che occorre assolutamente consocere per poterle utilizzare al meglio, e per poterle utilizzare senza generare errori grossolani. In particolare, è opportuno capire le dinamiche assunte dal valore "this" all’interno delle arrow functions, e quindi utilizzarle solo quando la situazione specifica lo richiede. Il fatto che le arrow functions siano, al momento della scrittura di questo articolo, la funzionalità ECMAScript 6 più popolare tra gli sviluppatori Javascript non deve trarre in inganno. Il mio consiglio è quello di studiare le arrow functions come funzionalità aggiuntiva alle funzioni comuni, ed utilizzarle nei contesti per cui sono state progettate ed implementate. Una collaborazione graduale e consapevole tra funzionalità passate e moderne permette di mantenere tutti i vantaggi delle normali funzioni esplicite (letterali, metodi, costruttori) e di usufruire di tutti quelli portati dalle benvenute arrow functions. Infine, è sempre bene controllare il grado di compatibilità offerto dalle varie versioni dei maggiori browser presenti in circolazione quando si decide di utilizzare una funzionalità ECMAScript 6, specialemente se la propria applicazione deve necessariamente essere utilizzata da versioni più o meno datate dei suddetti software.

Pubblicitร 

Leggi anche...

Infinite scroll, come programmarlo su AMP e su Web con Javascript

L'infinite scroll è una tecnica di design e navigazione...

Codice Fiscale: 5 javascript per la verifica e il calcolo

Il codice fiscale รจ un identificativo tributario univoco che...

Math.ceil() – Arrotondare per eccesso con Javascript

Il metodo ceil() dell'oggetto Math di Javascript è utilizzato...

Minificare Javascript: librerie e strumenti online per comprimere il sorgente JS

La minificazione è un processo abbastanza diffuso nell'implementazione delle...

Javascript: svuotare un campo input o una textarea con un click

Quando si fornisce agli utenti un modulo per l'inserimento...

6 video player HTML5 per il tuo sito web

Con il rilascio delle specifiche definitive per HTML5 molte...
Pubblicitร