Le funzioni in JavaScript hanno un ruolo importantissimo, sono definite a partire da una modalità (o paradigma) di programmazione che prende le mosse dal concetto di funzione in matematica e come vedremo permettono di definire gli oggetti.
Tuttavia tra i tanti modi di definire una funzione scegliamo qui quello forse più pragmatico:
una funzione è un insieme di istruzioni racchiuse in un blocco di codice, che può essere contraddistinto da un nome, può accettare argomenti o parametri di ingresso e restituire valori
Se la funzione ha un nome, esso servirà come riferimento per richiamare ed eseguire la funzione stessa, in qualunque punto del programma. Esistono però anche funzioni anonime, di cui parleremo più avanti.
A questo punto, secondo questa prima definizione, l'utilizzo di una funzione all'interno di uno script prevede due fasi:
- una fase di definizione o dichiarazione della funzione in cui si assegna un nome ad un blocco di codice;
- una fase di invocazione o chiamata in cui il blocco di codice viene eseguito.
Definire una funzione JavaScript
Lo schema sintattico per la definizione di una funzione in JavaScript è il seguente:
function nome(argomenti) {
// istruzioni
}
La parola chiave function evidenzia che stiamo dichiarando una funzione il cui nome
segue le stesse regole dei nomi di variabili. Gli argomenti
sono una lista opzionale di variabili separati da virgole che verranno utilizzate all'interno del corpo della funzione, cioè il blocco istruzioni
.
Una volta dichiarata, una funzione non viene eseguita subito. Stiamo semplicemente dicendo all'engine
JavaScript che al blocco di codice indicato viene assegnato un nome.
JS: Invocare una funzione
L'esecuzione vera e propria avviene con l'invocazione o chiamata, il cui schema sintattico è:
nome(valori);
dove nome
è il nome della funzione e valori
è una lista opzionale di espressioni separata da virgole.
Gli eventuali valori inseriti nella chiamata di una funzione vengono passati, cioè assegnati, ai corrispondenti argomenti della definizione della funzione.
L'istruzione return
Nel corpo della funzione può essere presente l'istruzione return che consente di terminare e restituire un valore al codice che l'ha chiamata. Questo ci consente di assegnare ad una variabile il valore restituito da una funzione o utilizzare una funzione all'interno di una espressione.
Esempi di funzioni JavaScript
Vediamo qualche esempio di definizione e chiamata di funzione:
function somma() {
var z = 11 + 5;
return z;
}
var risultato = somma();
In questo esempio abbiamo definito una funzione senza argomenti che somma due interi e restituisce il risultato. L'invocazione della funzione fa sì che venga eseguita la somma ed il risultato venga assegnato alla variabile risultato
.
La funzione somma()
che abbiamo definito può essere utilizzata in qualsiasi espressione e verrà trattata da JavaScript come un valore numerico:
var risultato = 5 + somma() * 2;
In questo esempio l'invocazione somma()
sarà equivalente al valore 16, per cui la variabile risultato
avrà valore 37
.
Argomenti o parametri di una funzione
La funzione somma()
che abbiamo definito prima è in grado di sommare soltanto i due numeri fissati nel blocco di istruzioni. Per renderla più generale è opportuno introdurre due argomenti che rappresenteranno i numeri da sommare:
function somma(x, y) {
var z = x + y;
return z;
}
In questo caso i valori da sommare verranno passati alla funzione somma()
al momento dell'invocazione:
var risultato = somma(11, 5);
Parametri opzionali
Come abbiamo avuto modo di vedere, JavaScript è un linguaggio molto flessibile e questa sua flessibilità ci consente di fare delle cose che talvolta possono apparire abbastanza strane. Ad esempio, a proposito degli argomenti di una funzione, non è obbligatorio passarli tutti in una chiamata di funzione.
In altre parole, data la definizione della funzione somma()
con i due argomenti da sommare, possiamo invocare la funzione nei seguenti modi:
var risultato1 = somma(11);
var risultato2 = somma();
var risultato3 = somma(11, 5, 7, 9);
Queste tre invocazioni della funzione somma()
potrebbero apparire errate, ma non generano nessun errore in JavaScript. In particolare, nel primo caso specifichiamo un solo argomento, il che assegna automaticamente al secondo argomento mancante il valore undefined
restituendo come somma il valore NaN
. Lo stesso ragionamento vale per il secondo caso, dove non passiamo alcun argomento.
Nel terzo caso, invece, passiamo più argomenti di quelli previsti nella definizione della funzione. JavaScript ignorerà gli argomenti in più restituendo lo stesso risultato di somma(11, 5)
.
L'array arguments
Una analoga flessibilità l'abbiamo anche nella dichiarazione degli argomenti quando definiamo una funzione. Ad esempio, possiamo non definire alcun argomento nella definizione di somma()
ed accedere ai valori passati in fase di chiamata tramite un array speciale predefinito: arguments
.
La definizione della nostra funzione si presenterebbe così:
function somma() {
var z = arguments[0] + arguments[1];
return z;
}
In questo caso non specifichiamo gli argomenti a fianco al nome della funzione, ma accediamo ad essi sfruttando questo array predefinito.
La disponibilità di arguments
ci consente di creare funzioni con un numero di parametri non definito. Ad esempio, possiamo sommare un numero indefinito di valori:
function somma() {
var z = 0;
var i;
for (i in arguments) {
z = z + arguments[i];
}
return z;
}
In questo modo potremo chiamare la funzione per sommare un qualsiasi numero di valori:
somma(2, 78);
somma(17, 32, 4, 19, 52);
Argomenti con valori di default
Con l'avvento di ECMAScript 6 vengono introdotti altri elementi che arricchiscono la flessibilità della gestione degli argomenti di una funzione. In particolare viene introdotta la possibilità di specificare dei valori di default:
function somma(x = 0, y = 0) {
var z = x + y;
return z;
}
In questo modo se al momento della chiamata non viene passato un argomento, ad esso viene assegnato il valore di default specificato, invece del valore undefined
. Quindi, ad esempio, la chiamata somma()
senza argomenti restituirà il valore 0
anzichè NaN
.
JavaScript: Rest parameter, argomenti aggiuntivi
Altra novità introdotta con la nuova versione dello standard è la possibilità di specificare il cosiddetto rest parameter
. Si tratta di una notazione speciale per indicare un elenco indefinito di argomenti aggiuntivi. Vediamo di cosa si tratta con un esempio.
Supponiamo di voler definire una funzione che implementa diverse operazioni aritmetiche e prende come primo argomento il nome dell'operazione da eseguire ed a seguire un numero variabile di valori su cui effettuare l'operazione. Possiamo definire tale funzione nel seguente modo:
function eseguiOperazione(x, ...y) {
var z = 0;
switch (x) {
case "somma":
for (i in y) {
z = z + y[i];
}
break;
case "moltiplica":
for (i in y) {
z = z * y[i];
}
break;
case "dividi":
z = y[0]/y[1];
break;
default:
z = NaN;
break;
}
return z;
}
Come possiamo vedere, abbiamo specificato un argomento x
che rappresenta il nome dell'operazione da eseguire e un argomento y
preceduto da tre punti che rappresenta il resto
dei valori da passare alla funzione.
La notazione dell'argomento preceduto dai puntini cattura l'elenco degli argomenti successivi al primo e lo rende disponibile all'interno della funzione sotto forma di array.
L'approccio è simile all'array predefinito arguments
, ma mentre questo cattura tutti gli argomenti della funzione, il rest parameter
cattura soltanto gli argomenti in più rispetto a quelli specificati singolarmente.
Possiamo quindi invocare la nostra funzione in uno dei seguenti modi:
eseguiOperazione("somma", 12, 54, 2, 7, 12);
eseguiOperazione("moltiplica", 4, 11, 32);
eseguiOperazione("dividi", 45, 9, 6, 17);
La prima e la seconda chiamata otterranno rispettivamente la somma ed il prodotto dei valori passati dal secondo argomento in poi, mentre la terza chiamata otterrà la divisione del secondo e terzo argomento, ignorando gli altri.
La stessa notazione del rest parameter
può essere utilizzata nelle chiamate a funzioni che prevedono diversi argomenti. In questo caso si parla di spread operator, cioè di un operatore che sparge i valori contenuti in un array sugli argomenti di una funzione, come nel seguente esempio:
var addendi = [8, 23, 19, 72, 3, 39];
somma(...addendi);
La chiamata con lo spread operator
è equivalente alla seguente chiamata:
somma(8, 23, 19, 72, 3, 39);
Ricordiamo che rest parameter
e spread operator
fanno parte delle specifiche di ECMAScript 6 e che quindi potrebbero non essere ancora supportati da tutti i browser.
Restituzione di valori
Abbiamo visto come una funzione possa restituire un valore al codice chiamante tramite l'istruzione return
. Questa istruzione può trovarsi soltanto all'interno del corpo di una funzione e può essere presente zero o più volte. Se una funzione non ha un valore da restituire, infatti, può omettere il return
o presentare l'istruzione senza un valore esplicito. In pratica le seguenti funzioni sono equivalenti:
function msg(messaggio) {
alert(messaggio);
}
function msg(messaggio) {
alert(messaggio);
return;
}
Se proviamo ad assegnare ad una variabile il valore restituito in entrambi i casi otterremo il valore undefined
.
L'effetto dell'esecuzione di return
è la terminazione della funzione e la restituzione di un valore. Quindi possiamo utilizzare l'istruzione più volte all'interno del corpo della funzione:
function decodifica(numero) {
switch(numero) {
case "uno":
return 1;
break;
case "due":
return 2;
break;
case "tre":
return 3;
break;
default:
return NaN;
break;
}
}
In questo esempio, la funzione termina e restituisce un valore non appena individua una corrispondenza con l'argomento, quindi le istruzioni break
sarebbero superflue nel caso specifico.
Tuttavia è buona norma limitare il numero di return
presenti in una funzione, riducendoli possibilmente a uno soltanto. Questo rende il codice più leggibile e manutenibile:
function decodifica(numero) {
var risultato;
switch(numero) {
case "uno":
risultato = 1;
break;
case "due":
risultato = 2;
break;
case "tre":
risultato = 3;
break;
default:
risultato = NaN;
break;
}
return risultato;
}