In JavaScript, una funzione non è una âstruttura magica del linguaggioâ, ma un valore speciale.
La sintassi che abbiamo utilizzato finora viene chiamata Dichiarazione di Funzione:
function sayHi() {
alert( "Hello" );
}
Eâ disponibile unâaltra sintassi per creare una funzione, chiamata function expression.
La sintassi:
let sayHi = function() {
alert( "Hello" );
};
Qui la funzione viene esplicitamente creata ed assegnata ad una variabile, proprio come un qualsiasi altro valore. Non ha importanza come la funzione viene definita, è solo un valore salvato nella variabile sayHi.
Il significato di questo esempio è lo stesso di: âcrea una funzione e mettila dentro la variabile sayHiâ.
Possiamo anche mostrarne il valore usando alert:
function sayHi() {
alert( "Hello" );
}
alert( sayHi ); // mostra il codice della funzione
Da notare che lâultima riga di codice non esegue la funzione, perché non ci sono parentesi dopo sayHi. Ci sono linguaggi di programmazione in cui la semplice menzione del nome di una funzione ne causa lâesecuzione; JavaScript non funziona così.
In JavaScript, una funzione è un valore, quindi possiamo lavorarci come con un qualsiasi altro valore. Il codice sopra ne mostra la sua rappresentazione in stringa, cioè il codice contenuto dentro la funzione.
Eâ chiaramente un valore speciale, poiché possiamo richiamarla con sayHi().
Ma rimane comunque un valore, e come tale possiamo trattarlo.
Possiamo copiare la funzione in unâaltra variabile:
function sayHi() { // (1) creazione
alert( "Hello" );
}
let func = sayHi; // (2) copia
func(); // Hello // (3) esegue la copia (funziona)!
sayHi(); // Hello // anche questa continua a funzionare (perché non dovrebbe?)
Quello che succede, nel dettaglio, è:
- La dichiarazione di funzione
(1)crea la funzione e la inserisce nella variabile denominatasayHi. - La linea
(2)la copia nella variabilefunc. Ancora una volta: non ci sono parentesi doposayHi. Se ci fossero state, allorafunc = sayHi()avrebbe inserito il risultato della chiamatasayHi(), non la funzionesayHi. - Adesso la funzione può essere richiamata sia con
sayHi()che confunc().
Avremmo anche potuto utilizzare, nella prima riga, una function expression per dichiarare sayHi:
let sayHi = function() { // (1) create
alert( "Hello" );
};
let func = sayHi;
// ...
Tutto funzionerebbe ugualmente. Risulta anche più chiaro cosa sta succedendo, giusto?
Vi starete chiedendo perché con la function expression bisogna mettere ; alla fine, mentre con la dichiarazione di funzione non serve:
function sayHi() {
// ...
}
let sayHi = function() {
// ...
};
La risposta è semplice:
- Non câè bisogno di
;alla fine dei blocchi di codice che utilizzano una sintassi del tipoif { ... },for { },function f { }etc. - Una function expression viene utilizzata allâinterno di unâistruzione:
let sayHi = ...;, come valore. Non è un blocco di codice. Quindi il;è consigliato al termine dellâistruzione, indipendentemente dal valore. Il punto e virgola non è collegato alla function expression; più semplicemente, termina unâistruzione.
Callback functions (funzioni richiamate)
Diamo unâocchiata ad ulteriori esempi di passaggio di funzione come valore e utilizzo della sintassi function expression.
Scriveremo una funzione ask(question, yes, no) con tre parametri:
question- Il testo della domanda
yes- Funzione da eseguire se la risposta è âYesâ
no- Funzione da eseguire se la risposta è âNoâ
La funzione dovrebbe richiedere la question e, in base alla risposta dellâutente, chiamare yes() o no():
function ask(question, yes, no) {
if (confirm(question)) yes()
else no();
}
function showOk() {
alert( "You agreed." );
}
function showCancel() {
alert( "You canceled the execution." );
}
// utilizzo: funzioni showOk, showCancel vengono passate come argomenti ad ask
ask("Do you agree?", showOk, showCancel);
Queste funzioni possono essere molto utili. La principale differenza tra unâimplementazione realistica e gli esempi sopra è che le funzioni ârealiâ utilizzano modalità più complesse per interagire con lâutente, non un semplice confirm. In ambiente browser, queste funzioni mostrano spesso delle finestre molto carine per gli input dellâutente. Ma questo è un altro discorso.
Gli argomenti showOk e showCancel della ask sono chiamati funzioni di richiamo o semplicemente callbacks.
Lâidea è di passare una funzione e di ârichiamarlaâ più tardi se necessario. Nel nostro caso showOk diventa la callback per la risposta âyesâ, e showCancel per la risposta ânoâ.
Possiamo utilizzare una function expression per scrivere la stessa funzione più concisamente:
function ask(question, yes, no) {
if (confirm(question)) yes()
else no();
}
ask(
"Do you agree?",
function() { alert("You agreed."); },
function() { alert("You canceled the execution."); }
);
Qui la funzione viene dichiarata dentro alla chiamata di ask(...). Queste non hanno nome, e perciò sono denominate anonime. Queste funzioni non sono accessibili dallâesterno di ask (perché non sono assegnate a nessuna variabile), ma è proprio quel che vogliamo in questo caso.
Questo tipo di codice comparirà nei nostri script molto naturalmente: è nello spirito di JavaScript.
I valori regolari come le stringhe o i numeri rappresentano dati.
Una funzione può anche essere vista come unâazione.
Possiamo passarla tra le variabili ed eseguirla quando vogliamo.
Function expression vs Dichiarazione di Funzioni
Cerchiamo di elencare le differenze chiave tra Dichiarazioni ed Espressioni di Funzione.
Primo, la sintassi: come distinguerle nel codice.
-
Dichiarazione di funzione: una funzione, dichiarata come unâistruzione separata, nel flusso principale del programma.
// Dichiarazione di funzione function sum(a, b) { return a + b; } -
Function expression: una funzione, creata allâinterno di unâespressione o allâinterno di un altro costrutto. Qui, la funzione è creata alla destra dellâ âespressione di assegnazioneâ
=:// function expression let sum = function(a, b) { return a + b; };
La differenza più subdola è quando una funzione viene creata dal motore JavaScript.
Una function expression viene creata quando lâesecuzione la raggiunge ed è utilizzabile solo da quel momento in poi.
Quando il flusso di esecuzione passa alla destra dellâoperatore di assegnamento let sum = function⦠, la funzione viene creata e, a partire da questo momento, può essere utilizzata (assegnata, chiamata, etcâ¦).
Una dichiarazione di funzione funziona diversamente.
Una dichiarazione di funzione è utilizzabile nellâintero script/blocco di codice.
In altre parole, quando JavaScript si prepara ad eseguire lo script o un blocco di codice, come prima cosa cerca le dichiarazioni di funzione e le crea. Possiamo pensare a questo processo come a uno âstage di inizializzazioneâ.
Eâ solo dopo che tutte le dichiarazioni di funzione sono state processate che lâesecuzione potrà procedere.
Come risultato, una funzione creata tramite una dichiarazione di funzione può essere richiamata anche prima della sua definizione.
Ad esempio, il seguente codice funziona:
sayHi("John"); // Hello, John
function sayHi(name) {
alert( `Hello, ${name}` );
}
La dichiarazione di funzione sayHi viene creata quando JavaScript si sta preparando ad eseguire lo script ed è visibile in ogni suo punto.
â¦Se fosse stata una function expression, non avrebbe funzionato:
sayHi("John"); // errore!
let sayHi = function(name) { // (*) nessuna magia
alert( `Hello, ${name}` );
};
Le espressioni di funzione sono create quando lâesecuzione le incontra. In questo esempio avverrà solo alla riga (*). Troppo tardi.
In strict mode, quando una dichiarazione di funzione viene fatta allâinterno di un blocco di codice sarà visibile ovunque allâinterno del blocco, ma non al suo esterno.
Qualche volta è comodo dichiarare funzioni locali, utili in un singolo blocco. Ma questa caratteristica potrebbe causare problemi.
Ad esempio, immaginiamo di aver bisogno di dichiarare una funzione welcome() (la utilizzeremo più avanti) in base ad un parametro age che valuteremo durante il runtime.
Il codice sotto non funziona:
let age = prompt("What is your age?", 18);
// dichiarazione di funzione condizionale
if (age < 18) {
function welcome() {
alert("Hello!");
}
} else {
function welcome() {
alert("Greetings!");
}
}
// ...utilizzo successivo
welcome(); // Errore: welcome non è definita
Questo accade perché una dichiarazione di funzione è visibile solamente allâinterno del blocco di codice in cui è stata definita.
Un altro esempio:
let age = 16; // prendiamo 16 come esempio
if (age < 18) {
welcome(); // \ (esegue)
// |
function welcome() { // |
alert("Hello!"); // | Dichiarazione di funzione disponibile
} // | ovunque nel blocco in cui è stata dichiarata
// |
welcome(); // / (esegue)
} else {
function welcome() {
alert("Greetings!");
}
}
// Qui siamo all'esterno delle parentesi graffe,
// quindi non possiamo vedere le dichiarazioni di funzione fatte al suo interno.
welcome(); // Errore: welcome non è definita
Cosa possiamo fare per rendere visibile welcome allâesterno del blocco if?
Il giusto approccio è quello di utilizzare una function expression ed assegnarla ad una variabile welcome, che viene dichiarata allâesterno di if ed ha quindi la corretta visibilità .
Ora funziona:
let age = prompt("What is your age?", 18);
let welcome;
if (age < 18) {
welcome = function() {
alert("Hello!");
};
} else {
welcome = function() {
alert("Greetings!");
};
}
welcome(); // ora funziona
Oppure possiamo semplificarla con lâutilizzo dellâoperatore ?:
let age = prompt("What is your age?", 18);
let welcome = (age < 18) ?
function() { alert("Hello!"); } :
function() { alert("Greetings!"); };
welcome(); // ora funziona
Come regola fondamentale, quando abbiamo la necessità di dichiarare una funzione, la prima opzione da considerare è la dichiarazione di funzione. Fornisce maggiore libertà per quanto riguarda lâorganizzazione del codice, poiché possiamo utilizzare la funzione anche prima della sua dichiarazione.
Risulta anche più facile vedere function f(â¦) {â¦}, nel codice, piuttosto di let f = function(â¦) {â¦}. La dichiarazione di funzione è più facile da individuare.
â¦Ma se per qualche ragione la dichiarazione di funzione non si applica bene al caso in questione (abbiamo visto degli esempi, sopra), allora la function expression può essere unâalternativa.
Riepilogo
- Le funzioni sono valori. Possono essere assegnate, copiate o dichiarate in qualsiasi punto del codice.
- Se la funzione viene dichiarata come un blocco âseparatoâ dal flusso dâesecuzione principale del codice, tale definizione viene chiamata âdichiarazione di funzioneâ.
- Se la funzione viene definita tramite unâespressione, viene chiamata âfunction expressionâ.
- Le dichiarazioni di funzione vengono processate prima che il blocco di codice dove sono state definite sia raggiunto.
- Le function expression vengono processate quando il flusso dâesecuzione del codice principale le raggiunge.
Nella maggior parte dei casi, quando abbiamo bisogno di dichiarare una funzione una dichiarazione di funzione è preferibile poiché è visibile anche prima della sua dichiarazione. Questo ci fornisce più flessibilità nellâorganizzazione del codice, e solitamente risulta più leggibile.
Dovremmo, quindi, utilizzare una function expression solo quando una dichiarazione di funzione non è adatta a un caso specifico. Abbiamo visto un paio di esempi in questo capitolo, e ne vederemo altri in futuro.
Commenti
<code>, per molte righe â includile nel tag<pre>, per più di 10 righe â utilizza una sandbox (plnkr, jsbin, codepenâ¦)