Le funzioni, in JavaScript, costituiscono un blocco di codice che si basa su dei dati e che può essere eseguito quando vogliamo.
Per farlo, ci basterà dichiarare la determinata funzione con i suoi parametri, il suo codice ed il suo nome.
Sommario
- Dichiarare una funzione
- Arrow functions
- Elementi di una funzione
- Nome della funzione
- Parametri
- Rest parameters
- Valori di default
- Codice della funzione
- Nested functions
- Funzioni asincrone
- JSDoc
- Function
- Metodi
- Funzioni globali
- Conclusione
Dichiarare una funzione
Per dichiarare una funzione, utilizziamo la parola chiave function
, che abbiamo già visto nell’articolo Parole chiave e operatori.
Ricontrolliamo la sintassi:
function a(/** b, ...c */) {
// Azioni da eseguire nella funzione
}
a
: Il nome della funzione;b, ...c
: I parametri della funzione.
Arrow functions
C’è poi un metodo diverso per dichiarare una funzione: le arrow functions.
Grazie a loro possiamo scrivere delle funzioni in modo più semplice che potranno essere eseguite più velocemente.
Vediamo i vantaggi e gli svantaggi di queste speciali funzioni:
Vantaggi:
- Più facili da utilizzare;
- Più veloci;
- Possibilità di restituire immediatamente un valore senza utilizzare le parentesi graffe.
Svantaggi:
- Non possono utilizzare
this
(vedremo questo più avanti); - Non possono essere utilizzati come metodi;
- Non possono essere usati per creare classi.
Sintassi:
(/** a, ...b */) => c; // Se abbiamo 0 o più parametri e vogliamo restituire direttamente un valore
(/** a, ...b */) => {
// Azioni da eseguire nella funzione
}; // Se abbiamo 0 o più parametri e vogliamo eseguire più di un\'azione
a, ...b
: Parametri facoltativi da passare nella funzione;c
: Valore da restituire immediatamente.
Esempi:
/**
* Funzione classica
* @param {number} a
*/
function name(a) {
return a + 100;
}
/**
* Equivalente:
* @param {number} a
*/
const name = (a) => a + 100;
/**
* Funzione classica
* @param {number} a
* @param {number} b
*/
function name1(a, b) {
return a + b + 100;
}
/**
* Equivalente:
* @param {number} a
* @param {number} b
*/
const name1 = (a, b) => a + b + 100;
// Funzione classica
const c = 4;
const d = 2;
function name2() {
return c + d + 100;
}
// Equivalente:
const name2 = () => c + d + 100;
/**
* Funzione classica
* @param {number} a
* @param {number} b
*/
function name3(a, b) {
const i = 42;
return a + b + i;
}
/**
* Equivalente:
* @param {number} a
* @param {number} b
*/
const name3 = (a, b) => {
const i = 42;
return a + b + i;
};
() => ({ foo: \"a\" }); // { foo: \"a\" }
/**
* @param {number} a
*/
const simple = (a) => (a > 15 ? 15 : a);
simple(16); // 15
simple(10); // 10
/**
* @param {number} a
* @param {number} b
*/
(a, b) => (a > b ? a : b);
const arr = [5, 6, 13, 0, 1, 18, 23];
arr.reduce((a, b) => a + b); // 66 - Vedremo gli array nel dettaglio in un altro articolo
arr.filter((v) => v % 2 === 0); // [6, 0, 18]
arr.map((v) => v * 2); // [10, 12, 26, 0, 2, 36, 46]
// Creiamo una Promise
// Vedremo questa struttura nel dettaglio in un altro articolo
const promise = Promise.resolve();
promise
.then((a) => {
console.log(a);
})
.catch((b) => {
console.error(b);
});
setTimeout(() => {
console.log(\"Prima\");
setTimeout(() => {
console.log(\"Dopo\");
}, 1);
}, 1);
Elementi di una funzione
Una funzione avrà quindi:
- Un nome (
a
); - Dei parametri facoltativi (
b, ...c
); - Un codice.
Andiamo ad esaminare questi elementi nel dettaglio.
Nome della funzione
Ogni funzione deve avere un nome.
Grazie ad esso saremo in grado di usarla, o chiamarla.
Il nome della funzione deve rispettare le stesse condizioni di quello delle variabili che abbiamo visto nell’articolo Variabili.
Ci sono casi in cui invece il nome è facoltativo, come nei callback, dove, però, utilizzeremo soprattutto le arrow functions:
setTimeout(() => console.log(\"Sus\"), 1); // Qui abbiamo utilizzato una arrow functions invece che una funzione normale
// Se volessimo utilizzare una funzione classica faremmo così:
setTimeout(function () {
return console.log(\"Sus\");
}, 1); // In questo caso possiamo omettere il nome della funzione
Le arrow functions, invece, non hanno un nome e le possiamo salvare in delle costanti:
const func = () => console.log(\"Sus\");
func();
// Console: \"Sus\"
Parametri
I parametri di una funzione sono come delle variabili dichiarate con let
all’interno della stessa funzione.
Possiamo decidere noi che nome dare ai parametri, così come quanti includerne nella nostra funzione e di quale tipo.
Esempi:
const sus = () => console.log(\"Sus\"); // Nessun parametro
/**
* Creiamo una funzione con un parametro.
* Usiamo il JSDoc per specificare il tipo del parametro.
* @param {unknown} a
*/
const log = (a) => console.log(a);
Rest parameters
Se in una funzione vogliamo utilizzare un numero indefinito di parametri, possiamo utilizzare i rest parameters.
Per farlo usiamo i 3 puntini prima del nome del parametro e nella funzione potremo accedere a quella variabile riferendoci ad un array dei parametri ricevuti.
Nota: è possibile utilizzare solo un rest parameter in una funzione e deve essere collocato obbligatoriamente alla fine.
Sintassi:
const a = (/** b, ..., */ ...c) => {
// Azioni da eseguire nella funzione
// `c` si riferirà ad un array di parametri passati
};
// Con una funzione classica:
function a(/** b, ..., */ ...c) {
// Azioni da eseguire nella funzione
// `c` si riferirà ad un array di parametri passati
}
c
: Nome da assegnare alla variabile che rappresenterà l’array di parametri passati alla fine (dopob
ed altri eventuali parametri).
Esempi:
/**
* @param {string} a
* @param {string} b
* @param {...string} otherParams - Rappresenta tutti i parametri passati nella funzione dopo `a` e `b`, quindi dal terzo in poi
*/
function myFun(a, b, ...otherParams) {
console.log(\"a\", a);
console.log(\"b\", b);
console.log(\"otherParams\", otherParams);
}
myFun(\"one\", \"two\", \"three\", \"four\", \"five\", \"six\");
// Console:
// a, \"one\"
// b, \"two\"
// otherParams, [\"three\", \"four\", \"five\", \"six\"]
myFun(\"one\", \"two\");
// Console:
// a, \"one\"
// b, \"two\"
// otherParams, [] - Abbiamo passato solo due parametri perciò `otherParams` sarà un array vuoto
Valori di default
Se un parametro è facoltativo potremmo voler aggiungere un valore di default che viene utilizzato quando quel parametro non viene passato.
Possiamo farlo, semplicemente usando l’uguale vicino al nome del parametro, come se volessimo assegnargli un valore.
Nota: è possibile utilizzare quanti valori di default vogliamo in una funzione ma devono essere collocati obbligatoriamente alla fine.
Sintassi:
function a(/** b, ..., */ c = d) {
// Azioni da eseguire nella funzione
}
c
: Il nome del parametro facoltativo;d
: Il valore di default che viene assegnato quando il parametro non viene passato o èundefined
.
Esempi:
/**
* Moltiplica 2 numeri.
* @param {number} a - Il numero da moltiplicare
* @param {number} [b] - Il numero per cui moltiplicare. 1 di default
*/
function multiply(a, b = 1) {
return a * b;
}
multiply(5, 2); // 10
multiply(5); // 5
multiply(5, undefined); // 5
/**
* Scriviamo in console il tipo del parametro passato
* @param {number|string|null} [num]
*/
function test(num = 1) {
console.log(typeof num);
}
test(); // \'number\' (num is 1)
test(undefined); // \'number\' (num is 1)
test(\"\"); // \'string\' (num is \'\')
test(null); // \'object\' (num is null)
Codice della funzione
Una funzione, infine, ha un codice.
Questo codice rappresenta le azioni da eseguire quando la funzione viene chiamata, utilizzando i parametri passati ad essa.
Dopo aver eseguito le dovute operazioni, la funzione potrà restituire un valore con la parola chiave return
.
Se nessun valore viene restituito, la funzione restituirà automaticamente undefined
.
Esempi:
/**
* Eseguiamo la somma di tutti i numeri passati come parametro.
* @param {...number} numbers - I numeri da sommare
*/
const sum = (...numbers) => numbers.reduce((num1, num2) => num1 + num2);
// Questa funzione non usa return ma è un arrow function abbreviata perciò il valore restituito è quello immediatamente dopo la freccia
/**
* Equivalente:
* @param {...number} numbers - I numeri da sommare
*/
(...numbers) => {
return numbers.reduce((num1, num2) => num1 + num2);
};
sum(12, 123, 55, 76); // 266
Nested functions
Si parla di funzioni annidate (o nested funnctions) quando una funzione viene dichiarata all’interno di un’altra.
In generale, si dovrebbe evitare di annidare più funzioni perchè questo porta solo a complicazioni nella lettura e interpretazione del codice, nonchè a un rallentamento nell’esecuzione.
Se dobbiamo utilizzare una funzione che utilizzi dei dati passati come parametri in un’altra funzione è bene utiizzare un arrow function e, se possibile, direttamente usarla dove ci serve, senza doverla dichiarare.
Esempi:
/**
* @param {string[]} args
*/
const run = (args) => {
const [first] = args;
return new Promise((resolve) => {
resolve(first);
});
// Abbiamo passato una funzione come parametro in `new Promise()` ma senza dichiararla all\'esterno
};
Funzioni asincrone
Le funzioni asincrone sono quelle che utilizzano async
prima del loro nome o, nelle arrow functions, prima dei parametri.
Queste funzioni possono utilizzare await
nel loro codice e restituiranno sempre una Promise
.
Esempi:
function resolveAfter2Seconds() {
console.log(\"starting slow promise\");
return new Promise((resolve) => {
setTimeout(() => {
resolve(\"slow\");
console.log(\"slow promise is done\");
}, 2000);
});
}
function resolveAfter1Second() {
console.log(\"starting fast promise\");
return new Promise((resolve) => {
setTimeout(() => {
resolve(\"fast\");
console.log(\"fast promise is done\");
}, 1000);
});
}
async function sequentialStart() {
// 1. Questo viene eseguito immediatamente
console.log(\"==SEQUENTIAL START==\");
const slow = await resolveAfter2Seconds();
console.log(slow); // 2. Questo viene eseguito 2 secondi dopo 1
const fast = await resolveAfter1Second();
console.log(fast); // 3. Questo viene eseguito 3 secondi dopo 1 e 1 secondo dopo 2
}
async function concurrentStart() {
// 1. Questo viene eseguito immediatamente
console.log(\"==CONCURRENT START with await==\");
const slow = resolveAfter2Seconds(); // Questo viene eseguito immediatamente
const fast = resolveAfter1Second(); // Questo viene eseguito immediatamente
// Non viene aspettato che il timer finisca utilizzando await
console.log(await slow); // 2. Questo viene eseguito 2 secondi dopo 1
console.log(await fast); // 3. Questo viene eseguito 2 secondi dopo 1 e immediatamente dopo 2 in quanto il timer di un secondo è già terminato
}
function concurrentPromise() {
// 1. Questo viene eseguito immediatamente
console.log(\"==CONCURRENT START with Promise.all==\");
return Promise.all([resolveAfter2Seconds(), resolveAfter1Second()]).then((messages) => {
console.log(messages[0]); // slow
console.log(messages[1]); // fast
// Vengono eseguiti entrambi dopo 2 secondi
});
}
sequentialStart();
setTimeout(concurrentStart, 4000);
setTimeout(concurrentPromise, 7000);
JSDoc
Come spiegato nel primo articolo, per dare informazioni al nostro editor riguardo i parametri delle nostre funzioni dobbiamo usare i JSDoc, ossia dei commenti multi-line prima di dichiarare la funzione.
Qui di seguito è illustrato come utilizzare questi commenti al meglio nelle funzioni.
Sintassi:
/**
* Descrizione della funzione
* @param {g} b - Descrizione del parametro `b`
* @param {h} [c] - Descrizione del parametro `c`. Utilizziamo le parentesi quadre per indicare che è facoltativo
* @param {i} [d] - Descrizione del parametro `d`. Se omettessimo questa riga il suo type sarà automaticamente rilevato dal valore di default
* @param {...j} f - Descrizione del parametro `e`. Utilizziamo i 3 puntini per indicare che è un rest parameter. Non c\'è bisogno di usare le parentesi quadre perchè i rest parameter sono sempre facoltativi
* @returns {k} Descrizione del valore restituito. Se omettessimo questa riga il suo type sarà automaticamente rilevato
*/
function a(b, c, d = e, ...f) {
// Azioni da eseguire nella funzione
}
g
: Il type del parametrob
;h
: Il type del parametro facoltativoc
. Non è necessario includereundefined
perchè viene incluso automaticamente nei parametri facoltativi;i
: Il type del parametro facoltativod
. Può essere omesso in quanto utilizziamo un valore di default. In tal caso il suo type sarebbe lo stesso die
;j
: Il type del rest parameterf
. Non è necessario specificare che si tratti di un array perchè viene rilevato automaticamente;k
: Il type del valore restituito. Può essere omesso se il nostro editor è capace di rilevarlo da solo.
Esempi:
/**
* Moltiplica 2 numeri.
* @param {number} a - Il numero da moltiplicare
* @param {number} [b] - Il numero per cui moltiplicare. 1 di default
* @returns {number} Il risultato dell\'operazione
*/
function multiply(a, b = 1) {
return a * b;
}
multiply(5, 2); // 10
multiply(5); // 5
multiply(5, undefined); // 5
/**
* Moltiplica più numeri per uno stesso valore.
* @param {number} multiplier - Il valore per cui moltiplicare
* @param {number[]} theArgs - I valori da moltiplicare
* @returns {number[]} I risultati
*/
function multiply2(multiplier, ...theArgs) {
return theArgs.map((x) => multiplier * x);
}
multiply2(2, 1, 2, 3); // [2, 4, 6]
Function
Ogni funzione appartiene ad una classe speciale, chiamata Function
.
Questa classe è globale ed accessibile ovunque nel nostro codice.
Nota: scopriremo nel dettaglio cosa sono le classi nel prossimo articolo, intanto, in questo paragrafo, vediamo quali sono i metodi comuni a tutte le funzioni.
Metodi
<Function>.bind(thisArg: unknown, ...args: unknown[]): Function
Questo metodo ci permette di creare una nuova funzione, identica alla precedente, ma con il valore di this
diverso e dei parametri appesi all’inizio di essa.
Sintassi:
func.bind(thisArg /** , ...args */);
thisArg
: L’object da usare comethis
;...args
: I parametri da passare come primi nella funzione.
Restituisce: func
– La funzione modificata con il nuovo this
e i parametri passati all’inizio.
Esempi:
const obj = {
a: 10,
/**
* Somma il valore di a con un altro.
* @param {number} b - Un altro numero
*/
sum(b) {
return this.a + b;
},
};
obj.sum(20); // 30 - `this.a + b` => `10 + 20` => 30
obj.sum.bind({ a: 15 })(25); // 40 - `this.a` corrisponde ora a 15
obj.sum.bind({ a: 35 }, 40)(); // 75 - `this.a` corrisponde ora a 35 e `b` corrisponde a 40
<Function>.call(thisArg: unknown, ...args: unknown[]): unknown
Questo metodo ci permette di chiamare una funzione, ma con il valore di this
diverso.
Sintassi:
func.call(thisArg /** , ...args */);
thisArg
: L’object da usare comethis
;...args
: I parametri da passare nella funzione.
Restituisce: unknown
– Il valore restituito dalla funzione.
Esempi:
const obj = {
a: 10,
/**
* Somma il valore di a con un altro.
* @param {number} b - Un altro numero
*/
sum(b) {
return this.a + b;
},
};
obj.sum(20); // 30 - `this.a + b` => `10 + 20` => 30
obj.sum.call({ a: 15 }, 25); // 40 - `this.a` corrisponde ora a 15 e `b` corrisponde a 25
obj.sum.call({ a: 35 }, 40); // 75 - `this.a` corrisponde ora a 35 e `b` corrisponde a 40
Funzioni globali
Ci sono poi delle funzioni predefinite che possiamo utilizzare ovunque.
Di seguito ne è una lista.
eval(x: string): unknown
Esegue un codice JavaScript rilevato da una stringa.
Nota: questa funzione può essere pericolosa, in quanto il codice potrebbe avere origini sconosciute e verrà eseguito normalmente.
Sintassi:
eval(x);
x
: Una stringa che contiene codice JavaScript valido.
Restituisce: unknown
– Il risultato dell’esecuzione del codice.
Esempi:
const x = 2;
const y = 39;
const z = \"42\";
eval(\"x + y + 1\"); // 42
eval(z); // 42
isFinite(number: number): boolean
Determina se un numero è finito (non infinito).
Sintassi:
isFinite(number);
number
: Un qualsiasi numero.
Restituisce: boolean
– true
se il numero passato non è Infinity
o NaN
, false
in caso contrario.
Esempi:
isFinite(Infinity); // false
isFinite(NaN); // false
isFinite(-Infinity); // false
isFinite(0); // true
isFinite(2e64); // true
isFinite(910); // true
isNaN(number: number): boolean
Determina se un numero è NaN (not-a-number).
Sintassi:
isNaN(number);
number
: Un qualsiasi numero.
Restituisce: boolean
– true
se il numero passato non è NaN
, false
in caso contrario.
Esempi:
isNaN(NaN); // true
isNaN(37); // false
parseFloat(string: string): number
Converte una stringa in un numero con la virgola.
Sintassi:
parseFloat(string);
string
: Una stringa che contiene un numero con la virgola.
Restituisce: number
– Il numero rilevato, o NaN
se non è stato possibile trovare alcun numero.
Esempi:
// Tutti i seguenti esempi restituiscono 3.14:
parseFloat(\"3.14\");
parseFloat(\" 3.14 \");
parseFloat(\"314e-2\");
parseFloat(\"0.0314E+2\");
parseFloat(\"3.14altri caratteri\");
parseInt(string: string, radix?: number): number
Converte una stringa in un numero con la virgola.
Sintassi:
parseInt(string /** , radix */);
string
: Una stringa che contiene un numero intero;radix
: La base del numero (decimale, ottale, etc…). Facoltativo, è 10 di default se il numero non inizia con 0 e 16 in caso contrario.
Restituisce: number
– Il numero rilevato, o NaN
se non è stato possibile trovare alcun numero.
Esempi:
// Tutti i seguenti esempi restituiscono 15:
parseInt(\"015\", 10);
parseInt(\"15,123\", 10);
parseInt(\"15 * 3\", 10);
parseInt(\"15e2\", 10);
parseInt(\"15px\", 10);
parseInt(\"12\", 13);
encodeURI(uri: string): string
Codifica una stringa in formato URI.
Sintassi:
encodeURI(uri);
uri
: Un URI.
Restituisce: string
– La stringa convertita in formato URI.
Esempi:
encodeURI(\";,/?:@&=+$#\"); // ;,/?:@&=+$#
encodeURI(\"-_.!~*\'()\"); // -_.!~*\'()
encodeURI(\"ABC abc 123\"); // ABC%20abc%20123
decodeURI(encodedURI: string): string
Decodifica una stringa dal formato URI.
Sintassi:
decodeURI(encodedURI);
encodedURI
: Un URI codificato.
Restituisce: string
– La stringa decodificata in formato URI.
Esempi:
decodeURI(\"https://developer.mozilla.org/ru/docs/JavaScript_%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\");
// \"https://developer.mozilla.org/ru/docs/JavaScript_шеллы\"
Conclusione
In questo articolo abbiamo osservato le funzioni e il loro utilizzo!
Ora sappiamo come creare una funzione ed utilizzare i suoi parametri.
Nel prossimo articolo vedremo invece le classi, una specie di incrocio tra le funzioni e gli object!
Lascia un commento