Come diversi linguaggi di programmazione, anche JavaScript supporta la reflection
, cioè la capacità di analizzare a runtime la struttura degli elementi e dei dati di un programma.
La natura dinamica del linguaggio ci consente sia di analizzare che di modificare la struttura degli oggetti durante l'esecuzione. Dal momento che un oggetto può essere visto come una sorta di array associativo possiamo iterare tra i suoi membri sfruttando un semplice for
o una delle sue varianti, for...in
e for...of
.
Ad esempio, se consideriamo il seguente oggetto:
var persona = {
nome: "Mario",
cognome: "Rossi",
mostraNomeCompleto: function() {
return this.nome + " " + this.cognome;
}
};
possiamo elencare i suoi membri con un ciclo analogo al seguente:
var p;
for (p in persona) { console.log(p); }
Il risultato che otterremo sarà l'elenco dei nomi delle proprietà e dei metodi dell'oggetto persona
.
Utilizzando il costrutto for...in
, ad ogni ciclo otteniamo il nome del membro di un oggetto. Se vogliamo elencare i valori associati a ciascun membro possiamo farlo nel seguente modo:
for (p in persona) { console.log(persona[p]); }
In alternativa possiamo utilizzare il costrutto for...of
che ci consente di accedere ad ogni ciclo direttamente al valore del membro dell'oggetto su cui stiamo iterando.
Se vogliamo distinguere tra proprietà e metodi di un oggetto possiamo utilizzare l'operatore typeof
che restituisce una stringa che identifica il tipo dell'elemento analizzato. Ad esempio, il seguente ciclo restituisce soltanto i metodi di un oggetto:
for (p in persona) {
if (typeof persona[p] == "function") console.log(p);
}
mentre il seguente ciclo restituisce soltanto le proprietà:
for (p in persona) {
if (typeof persona[p] != "function") console.log(p);
}
Oltra a function
, tra i possibili valori restituiti dall'operatore typeof
abbiamo undefined
, object
, boolean
, number
e string
, il cui significato è abbastanza evidente.
Oltre all'utilizzo nel costrutto for...in, l'operatore in
può essere utilizzato per verificare a runtime la presenza di una determinata proprietà di un oggetto. L'espressione
'propietà' in oggetto
restituisce un valore booleano, pari a true nel caso in cui l'oggetto specificato contenga la proprietà indicata, e pari a false in caso contrario. Ad esempio:
if('nome' in persona) {
console.log(persona.nome);
} else {
console.log("L'oggetto persona non contiene una proprietà 'nome'");
}
L'operatore in restituisce true anche nel caso di proprietà ereditate da un prototipo.
Gli esempi che abbiamo visto ci consentono di analizzare tutte le proprietà e i metodi di un oggetto JavaScript. Dal momento però che un oggetto può avere proprietà o metodi derivati da un prototipo, potremmo essere interessati a fare una distinzione tra quelli che sono i membri specifici dell'oggetto in questione e quelli che derivano invece da uno degli oggetti della propria catena di prototipi.
Ci viene incontro in questo caso il metodo hasOwnProperty() disponibile per tutti gli oggetti JavaScript:
for (p in persona) {
if (typeof persona[p] != "function" && persona.hasOwnProperty(p))
console.log(p);
}
Questo esempio restituisce i nomi delle sole proprietà non derivate da un prototipo.
Possiamo analizzare la relazione tra un oggetto ed il suo prototipo sfruttando l'operatore instanceof
:
marioRossi instanceof persona
Questa espressione restituisce true
se l'oggetto marioRossi
è un'istanza del costruttore persona()
, false
altrimenti.
Infine, il metodo Object.getPrototypeOf() ci consente di ottenere il prototipo di un oggetto:
var prototipo = Object.getPrototypeOf(marioRossi);