Espressioni di Definizione e Invocazione di Funzioni in Javascript
- Le espressioni di definizione di funzione consentono di creare funzioni in modo flessibile e dinamico.
- Le espressioni di invocazione di funzione sono utilizzate per eseguire funzioni e passare argomenti.
- Le funzioni possono essere definite in vari modi, inclusi le espressioni di funzione e le arrow function.
Espressioni di Definizione di Funzione
Un'espressione di definizione di funzione definisce una funzione JavaScript, e il valore di tale espressione è la funzione appena definita. In un certo senso, un'espressione di definizione di funzione è un "letterale di funzione" nello stesso modo in cui un inizializzatore di oggetto è un "letterale di oggetto." Un'espressione di definizione di funzione consiste tipicamente nella parola chiave function
seguita da un elenco separato da virgole di zero o più identificatori (i nomi dei parametri) tra parentesi e un blocco di codice JavaScript (il corpo della funzione) tra parentesi graffe. Ad esempio:
// Questa funzione restituisce il quadrato del valore passato ad essa.
let quadrato = function(x) { return x * x; };
Un'espressione di definizione di funzione può anche includere un nome per la funzione. Le funzioni possono anche essere definite usando un'istruzione di funzione piuttosto che un'espressione di funzione. E in ES6 e versioni successive, le espressioni di funzione possono usare una nuova sintassi compatta chiamata arrow function o funzione freccia. I dettagli completi sulla definizione di funzione li vedremo nelle prossime lezioni.
Espressioni di Invocazione
Un'espressione di invocazione è la sintassi di JavaScript per chiamare (o eseguire) una funzione o un metodo. Inizia con un'espressione di funzione che identifica la funzione da chiamare. L'espressione di funzione è seguita da una parentesi aperta, una lista separata da virgole di zero o più espressioni di argomento, e una parentesi chiusa. Alcuni esempi:
f(0) // f è l'espressione di funzione; 0 è l'espressione di argomento.
Math.max(x,y,z) // Math.max è la funzione; x, y, e z sono gli argomenti.
a.sort() // a.sort è la funzione; non ci sono argomenti.
Quando un'espressione di invocazione viene valutata, l'espressione di funzione viene valutata per prima, e poi le espressioni di argomento vengono valutate per produrre una lista di valori di argomento. Se il valore dell'espressione di funzione non è una funzione, viene lanciato un TypeError
. Successivamente, i valori di argomento vengono assegnati, in ordine, ai nomi dei parametri specificati quando la funzione è stata definita, e poi il corpo della funzione viene eseguito. Se la funzione usa un'istruzione return
per restituire un valore, allora quel valore diventa il valore dell'espressione di invocazione. Altrimenti, il valore dell'espressione di invocazione è undefined
. I dettagli completi sull'invocazione di funzioni, inclusa una spiegazione di cosa succede quando il numero di espressioni di argomento non corrisponde al numero di parametri nella definizione della funzione, sono presentati nelle prossime lezioni.
Ogni espressione di invocazione include una coppia di parentesi e un'espressione prima della parentesi aperta. Se quell'espressione è un'espressione di accesso a proprietà, allora l'invocazione è conosciuta come invocazione di metodo. Nelle invocazioni di metodo, l'oggetto o array che è il soggetto dell'accesso alla proprietà diventa il valore della parola chiave this
mentre il corpo della funzione viene eseguito. Questo abilita un paradigma di programmazione orientata agli oggetti in cui le funzioni (che chiamiamo "metodi" quando usate in questo modo) operano sull'oggetto di cui fanno parte. Analizzeremo i dettagli quando studieremo le classi nelle prossime lezioni.
Invocazione Condizionale
In ES2020, è possibile invocare una funzione utilizzando ?.()
invece di ()
. Normalmente quando si invoca una funzione, se l'espressione a sinistra delle parentesi è null
o undefined
o qualsiasi altro oggetto non-funzione, viene lanciato un TypeError
. Con la nuova sintassi di invocazione ?.()
, se l'espressione a sinistra di ?.
valuta a null
o undefined
, allora l'intera espressione di invocazione valuta a undefined
e non viene lanciata alcuna eccezione.
Gli oggetti Array hanno un metodo sort()
che può opzionalmente ricevere un argomento funzione che definisce l'ordine di ordinamento desiderato per gli elementi dell'array. Prima di ES2020, se volevamo scrivere un metodo come sort()
che prende un argomento funzione opzionale, usavamo tipicamente un'istruzione if
per verificare che l'argomento funzione fosse definito prima di invocarlo nel corpo dell'if
:
function quadrato(x, log) { // Il secondo argomento è una funzione opzionale
if (log) { // Se la funzione opzionale viene passata
log(x); // Invocala
}
return x * x; // Restituisce il quadrato dell'argomento
}
Con questa sintassi di invocazione condizionale di ES2020, tuttavia, è possibile semplicemente scrivere l'invocazione della funzione utilizzando ?.()
, sapendo che l'invocazione avverrà solo se c'è effettivamente un valore da invocare:
function quadrato(x, log) { // Il secondo argomento è una funzione opzionale
log?.(x); // Chiama la funzione se ce n'è una
return x * x; // Restituisce il quadrato dell'argomento
}
Si noti, tuttavia, che ?.()
verifica solo se il lato sinistro è null
o undefined
. Non verifica che il valore sia effettivamente una funzione. Quindi la funzione quadrato()
in questo esempio lancerebbe comunque un'eccezione se le si passassero due numeri, per esempio.
Come le espressioni di accesso condizionale alle proprietà viste in precedenza, l'invocazione di funzione con ?.()
è a corto-circuito: se il valore a sinistra di ?.
è null
o undefined
, allora nessuna delle espressioni argomento all'interno delle parentesi viene valutata:
let f = null, x = 0;
try {
f(x++); // Lancia `TypeError` perché f è null
} catch(e) {
x // => 1: x viene incrementato prima che venga lanciata l'eccezione
}
f?.(x++) // => undefined: f è null, ma non viene lanciata alcuna eccezione
x // => 1: l'incremento viene saltato a causa del corto-circuito
Le espressioni di invocazione condizionale con ?.()
funzionano altrettanto bene per i metodi come per le funzioni. Ma poiché l'invocazione di metodi coinvolge anche l'accesso alle proprietà, vale la pena prenderci un momento per essere sicuri di comprendere le differenze tra le seguenti espressioni:
o.m() // Accesso regolare alle proprietà, invocazione regolare
o?.m() // Accesso condizionale alle proprietà, invocazione regolare
o.m?.() // Accesso regolare alle proprietà, invocazione condizionale
Nella prima espressione, o
deve essere un oggetto con una proprietà m
e il valore di quella proprietà deve essere una funzione. Nella seconda espressione, se o
è null
o undefined
, allora l'espressione valuta a undefined
. Ma se o
ha qualsiasi altro valore, allora deve avere una proprietà m
il cui valore è una funzione. E nella terza espressione, o
non deve essere null
o undefined
. Se non ha una proprietà m
, o se il valore di quella proprietà è null
, allora l'intera espressione valuta a undefined
.
L'invocazione condizionale con ?.()
è una delle caratteristiche più recenti di JavaScript. A partire dai primi mesi del 2020, questa nuova sintassi è stata introdotta nella maggior parte dei browser principali.