Molto spesso abbiamo la necessit di eseguire azioni simili in diverse parti dello script.
Ad esempio, vogliamo mostrare un messaggio quando un utente effettua il login/logout o per qualsiasi altro motivo.
Le funzioni sono le fondamenta di un programma. Consentono a un codice di essere utilizzato pi volte, evitando ripetizioni.
Abbiamo gi visto esempi di funzioni integrate nel linguaggio, come alert(message), prompt(message, default) e confirm(question). Ma possiamo benissimo anche creare delle nostre funzioni personali.
Dichiarazione di Funzione
Per creare una funzione dobbiamo utilizzare una dichiarazione di funzione.
Che appare cosi:
function showMessage() {
alert( 'Hello everyone!' );
}
La parola chiave function va posta allinizio; viene seguita dal nome della funzione, poi c una lista di parametri, racchiusi tra parentesi (separati da virgola, in questo esempio la lista vuota, vedremo un esempio pi avanti) e infine il codice della funzione, chiamato anche corpo della funzione, racchiuso tra parentesi graffe.
function name(parameter1, parameter2, ... parameterN) {
...body...
}
La nostra nuova funzione pu essere chiamata tramite il suo nome: showMessage().
Ad esempio:
function showMessage() {
alert( 'Hello everyone!' );
}
showMessage();
showMessage();
La chiamata showMessage() esegue il codice dentro la funzione. Qui vedremo il messaggio due volte.
Questo esempio mostra chiaramente uno dei principali scopi delle funzioni: evitare le ripetizioni di un codice.
Se avremo la necessit di cambiare il messaggio o il modo in cui viene mostrato, sar sufficiente modificare il codice in un solo punto: la funzione che lo implementa.
Variabili locali
Una variabile dichiarata allinterno di una funzione visibile solamente allinterno di quella funzione.
Ad esempio:
function showMessage() {
let message = "Hello, I'm JavaScript!"; // variabile locale
alert( message );
}
showMessage(); // Hello, I'm JavaScript!
alert( message ); // <-- Errore! La variabile locale alla funzione
Variabili esterne
Una funzione pu accedere ad una variabile esterna, ad esempio:
let userName = 'John';
function showMessage() {
let message = 'Hello, ' + userName;
alert(message);
}
showMessage(); // Hello, John
La funzione ha pieno accesso alla variabile esterna. Pu anche modificarla.
Un esempio:
let userName = 'John';
function showMessage() {
userName = "Bob"; // (1) cambiata la variabile esterna
let message = 'Hello, ' + userName;
alert(message);
}
alert( userName ); // John prima della chiamata di funzione
showMessage();
alert( userName ); // Bob, il valore stato modificato dalla funzione
La variabile esterna viene utilizzata solo se non ce n una locale.
Se una variabile con lo stesso nome viene dichiarata allinterno di una funzione, questa oscurer quella esterna. Ad esempio, nel codice sotto la funzione usa la variabile locale userName. Quella esterna viene ignorata:
let userName = 'John';
function showMessage() {
let userName = "Bob"; // dichiara una variabile locale
let message = 'Hello, ' + userName; // Bob
alert(message);
}
// la funzione creer utilizzer un suo 'personale' userName
showMessage();
alert( userName ); // John, intoccato, la funzione non pu accedere alla variabile esterna
Le variabili dichiarate allesterno di qualsiasi funzione, come userName nel codice sopra, vengono chiamate globali.
Le variabili globali sono visibili a qualsiasi funzione (se non sono oscurate da quelle locali).
Solitamente, una funzione dichiara internamente tutte le variabili necessarie per svolgere il suo compito. Le variabili locali vengono utilizzate per memorizzare dati relativi alla funzione stessa, quando importante che queste non siano accessibili in qualsiasi punto del codice. I codici moderni cercano di evitare le variabili globali, sebbene qualche volta possano essere utili per dati
Parametri
Possiamo passare dei dati arbitrari ad una funzione usando i parametri.
Nellesempio sotto, la funzione ha due parametri: from e text.
function showMessage(from, text) { // parametri: from, text
alert(from + ': ' + text);
}
showMessage('Ann', 'Hello!'); // Ann: Hello! (*)
showMessage('Ann', "What's up?"); // Ann: What's up? (**)
Quando la funzione viene chiamata nelle righe (*) e (**), il valore passato viene copiato nelle variabili locali from e text, che verranno utilizzate nella chiamata ad alert.
Guardiamo un altro esempio: abbiamo una variabile from e la passiamo a una funzione. Da notare: la funzione cambia from, ma il cambiamento non visibile allesterno perch la funzione usa sempre una copia del valore passato:
function showMessage(from, text) {
from = '*' + from + '*'; // rende "from" pi carino
alert( from + ': ' + text );
}
let from = "Ann";
showMessage(from, "Hello"); // *Ann*: Hello
// il valore di "from" lo stesso, la funzione ne ha modificato una copia locale
alert( from ); // Ann
Quando un valore viene passato come parametro di funzione, vine anche chiamato argomento.
In altre parole In other words, per chiarire questi termini:
- Un parametro la variabile elencata tra parentesi nella dichiarazione della funzione (fa parte della dichiarazione).
- Un argomento il valore passato alla funzione quando viene chiamata (fa parte della chiamata).
Dichiariamo le funzioni elencando i loro parametri, quindi le chiamiamo passando gli argomenti.
Nellesempio sopra, si potrebbe dire: la funzione showMessage dichiarata con due parametri, quindi viene chiamata con due argomenti: from and "Hello".
Valori di default
Se non viene fornito alcun parametro, questa assume il valore undefined.
Ad esempio, la funzione menzionata sopra showMessage(from, text) pu essere chiamata con un solo argomento:
showMessage("Ann");
Questo non un errore. Una chiamata simile mostrer "*Ann*: undefined". Siccome non viene passato nessun valore per text, questo undefined.
Possiamo specificare un cosiddetto valore default (da usare se largomento omesso) per i parametri nella dichiarazione di funzione, usando =:
function showMessage(from, text = "no text given") {
alert( from + ": " + text );
}
showMessage("Ann"); // Ann: nessun text fornito
Adesso, se il parametro text non viene passato, assumer il valore "no text given"
In questo caso "no text given" una stringa, ma potrebbe essere unespressione pi complessa, che viene valutata e assegnata solamente se il parametro non viene fornito. Quindi, possibile anche:
function showMessage(from, text = anotherFunction()) {
// anotherFunction() viene eseguita solamente se non viene fornito text
// il risultato diventa il valore di text
}
In JavaScript, un parametro di default viene valutato ogni volta che viene chiamata una funzione senza i rispettivo argomento.
Nellesempio sopra, anotherFunctions() non viene chiamata se viene passato il parametro text.
Viene invece chiamata ogni volta che il parametro manca.
A volte ha senso assegnare valori default ai parametri, non nella dichiarazione della funzione, ma in una fase successiva.
Possiamo verificare se il parametro viene passato durante lesecuzione della funzione, confrontandolo con undefined:
function showMessage(text) {
// ...
if (text === undefined) { // if the parameter is missing
text = 'empty message';
}
alert(text);
}
showMessage(); // empty message
Oppure utilizzare loperatore ||:
function showMessage(from, text) {
// se text falso allora text assume il valore di default
text = text || 'no text given';
...
}
I moderni motori JavaScript supportano il nullish coalescing operator ??, pi efficiente quando valori falsi come 0 vengono considerati regolari:
//se non c' un parametro "count", mostra "unknown"
function showCount(count) {
// if count is undefined or null, show "unknown"
alert(count ?? "unknown");
}
showCount(0); // 0
showCount(null); // unknown
showCount(); // unknown
Ritornare un valore
Una funzione pu anche ritornare, come risultato, un valore al codice che ha effettuato la sua chiamata.
Un semplicissimo esempio una funzione che somma due valori:
function sum(a, b) {
return a + b;
}
let result = sum(1, 2);
alert( result ); // 3
La direttiva return pu trovarsi in qualsiasi punto della funzione. Quando lesecuzione incontra questa direttiva, si ferma, e il valore viene ritornato al codice che ha chiamato la funzione (nel codice sopra viene assegnato a result).
Ci possono essere pi occorrenze di return in una singola funzione. Ad esempio:
function checkAge(age) {
if (age >= 18) {
return true;
} else {
return confirm('Do you have permission from your parents?');
}
}
let age = prompt('How old are you?', 18);
if ( checkAge(age) ) {
alert( 'Access granted' );
} else {
alert( 'Access denied' );
}
E anche possibile utilizzare return senza alcun valore. Questo causer luscita immediata dalla funzione.
Ad esempio:
function showMovie(age) {
if ( !checkAge(age) ) {
return;
}
alert( "Showing you the movie" ); // (*)
// ...
}
Nel codice sopra, se checkAge(age) ritorna false, allora showMovie non proceder fino ad alert.
return vuoto ritorna undefinedSe una funzione non ritorna alcun valore, come se ritornasse undefined:
function doNothing() { /* vuoto */ }
alert( doNothing() === undefined ); // true
Un return vuoto dunque la stessa cosa di return undefined:
function doNothing() {
return;
}
alert( doNothing() === undefined ); // true
return e il valorePer espressioni lunghe dopo la direttiva return, si potrebbe essere tentati dal metterle in una nuova riga, come:
return
(some + long + expression + or + whatever * f(a) + f(b))
Questo non funziona, perch JavaScript interpreta un punto e virgola dopo return. E come se dopo return ci fosse scritto:
return;
(some + long + expression + or + whatever * f(a) + f(b))
Quindi, diventerebbe a tutti gli effetti un return vuoto. Dobbiamo quindi posizionare il valore da ritornare nella stessa riga.
Denominare una funzione
Le funzioni sono azioni. Solitamente, per il loro nome vengono utilizzati verbi. Dovrebbero essere brevi, il pi accurati possibili e descrittivi di ci che la funzione fa. Un estraneo che legge il codice deve essere in grado di capire ci che fa la funzione.
Una pratica molto diffusa quella di iniziare il nome con un verbo, come prefisso, che descriva vagamente lazione. Deve esserci un accordo con il team riguardo il significato dei prefissi.
Ad esempio, le funzioni che iniziano con "show" solitamente mostrano qualcosa.
Funzioni che iniziano per
"get"ritornano un valore,"calc"calcolano qualcosa,"create"creano qualcosa,"check"controllano qualcosa e ritornano un booleano, etc.
Esempi di nomi:
showMessage(..) // mostra un messaggio
getAge(..) // ritorna l'et (prendendola da qualche parte)
calcSum(..) // calcola la somma e ritorna il risultato
createForm(..) // crea un form (e solitamente lo ritorna)
checkPermission(..) // controlla i permessi, ritorna true/false
Tramite il prefisso, una semplice occhiata al nome della funzione dovrebbe far capire che tipo di lavoro eseguir e quale sar il tipo di valore ritornato.
Una funzione dovrebbe fare esattamente ci che il suo nome descrive, niente di pi.
Due azioni separate solitamente meritano due funzioni diverse, anche se molto spesso vengono chiamate insieme (in questo caso potrebbe essere utile creare una terza funzione che chiama entrambe).
Un paio di esempi che non rispettano queste regole:
getAgesarebbe un pessimo nome se mostrasse unalertcon let (dovrebbe solo restituirlo).createFormsarebbe un pessimo nome se modificasse il documento, aggiungendo il form (dovrebbe solo crearlo e restituirlo).checkPermissionsarebbe un pessimo nome se mostrasse il messaggioaccess granted/denied(dovrebbe solo eseguire il controllo e ritornare il risultato).
Questi esempi assumono i significati comuni dei prefissi. Il loro significato dipende da voi e dal vostro team. E normale che il tuo codice abbia caratteristiche diverse, ma fondamentale avere una firma il cui prefisso sia sensato, che faccia capire cosa un determinato tipo di funzione pu o non pu fare. Tutte le funzioni che iniziano con lo stesso prefisso dovrebbero seguire determinate regole. E fondamentale che il team condivida queste informazioni.
Funzioni == Commenti
Le funzioni dovrebbero essere brevi ed eseguire un solo compito. Se invece risultano lunghe, forse varrebbe la pena spezzarle in funzioni pi piccole. Qualche volta pu non essere semplice seguire questa regola, anche se sarebbe la cosa migliore.
Una funzione separata non solo semplice da testare e correggere la sua stessa esistenza un commento!
Ad esempio, osserviamo le due funzioni showPrimes(n) sotto . Entrambe ritornano i numeri primi fino a n.
La prima versione utilizza le etichette:
function showPrimes(n) {
nextPrime: for (let i = 2; i < n; i++) {
for (let j = 2; j < i; j++) {
if (i % j == 0) continue nextPrime;
}
alert( i ); // un primo
}
}
La seconda variante utilizza un funzione addizionale isPrime(n) per testare se un numero rispetta la condizione di essere primo:
function showPrimes(n) {
for (let i = 2; i < n; i++) {
if (!isPrime(i)) continue;
alert(i); // un primo
}
}
function isPrime(n) {
for (let i = 2; i < n; i++) {
if ( n % i == 0) return false;
}
return true;
}
La seconda variante pi semplice da capire, non vi pare? Invece di un pezzo di codice vediamo il nome dellazione (isPrime). Talvolta le persone si riferiscono a questo tipo di codice come auto descrittivo.
Quindi, le funzioni possono essere create anche se non abbiamo intenzione di riutilizzarle. Infatti forniscono una struttura migliore al codice e lo rendono pi leggibile.
Riepilogo
La dichiarazione di una funzione si scrive cos:
function name(parameters, delimited, by, comma) {
/* codice */
}
- Valori passati ad una funzione come parametri vengono copiati in variabili locali.
- Una funzione pu avere accesso alle variabili esterne. Questo meccanismo funziona solo dallinterno verso lesterno. Il codice esterno non pu vedere le variabili locali ad una funzione.
- Una funzione pu ritornare un valore. Se non lo fa esplicitamente, questo sar
undefined.
Per rendere il codice pulito e pi facile da leggere, consigliabile utilizzare principalmente variabili locali e parametri di funzione, non variabili esterne.
E sempre pi facile capire una funzione che accetta parametri, li lavora e ritorna un valore piuttosto di una funzione che non richiede parametri ma, come effetto collaterale, modifica variabili esterne.
Denominare le funzioni:
- Un nome dovrebbe descrivere chiaramente ci che una funzione fa. Quando vediamo la chiamata di una funzione nel codice, se questa ha un buon nome ci far immediatamente capire il suo comportamento e cosa ritorner.
- Una funzione un azione, quindi i nomi delle funzioni utilizzano molto spesso dei verbi.
- Esistono molti prefissi comuni come
create,show,get,checke molti altri. Utilizzateli per descrivere il comportamento di una funzione.
Le funzioni sono il principale blocco di un codice. Ora che abbiamo studiato le basi possiamo iniziare a crearle e utilizzarle. Ma questo solo linizio. Ci ritorneremo molto spesso, andando molto in profondit, per capirne bene le caratteristiche.
Commenti
<code>, per molte righe includile nel tag<pre>, per pi di 10 righe utilizza una sandbox (plnkr, jsbin, codepen)