Loperatore instanceof ci consente di verificare se un oggetto appartiene ad una specifica classe. Anche lereditariet viene presa in considerazione.
Questo tipo di controllo potrebbe essere necessario in diversi casi. Ad esempio, pu essere utilizzato per costruire una funzione polimorfa, ossia una funzione che tratta gli argomenti differentemente in base al loro tipo.
L'operatore instanceof
La sintassi :
obj instanceof Class
Ritorna true se obj di tipo Class o una sua sotto-classe.
Ad esempio:
class Rabbit {}
let rabbit = new Rabbit();
// un oggetto di tipo Rabbit?
alert( rabbit instanceof Rabbit ); // true
Funziona anche con i costruttori:
// invece di usare class
function Rabbit() {}
alert( new Rabbit() instanceof Rabbit ); // true
E con le classi integrate come Array:
let arr = [1, 2, 3];
alert( arr instanceof Array ); // true
alert( arr instanceof Object ); // true
Da notare che arr appartiene anche alla classe Object. Questo accade perch Array eredita da Object.
Normalmente instanceof esamina la catena dei prototype per effettuare questa verifica. Possiamo anche definire una logica personalizzata nel metodo statico Symbol.hasInstance.
Lalgoritmo di obj instanceof Class funziona, a grandi linee, in questo modo:
-
Se stato definito un metodo statico
Symbol.hasInstance, allora questo verr invocato:Class[Symbol.hasInstance](obj). Dovrebbe ritornaretrueofalse, questo tutto. In questo modo possiamo personalizzare il comportamento diinstanceof.For example:
// impostiamo il controllo instanceOf in modo che assuma che // qualsiasi cosa con la propriet canEat sia un animale class Animal { static [Symbol.hasInstance](obj) { if (obj.canEat) return true; } } let obj = { canEat: true }; alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) stato invocato -
Molte classi non hanno
Symbol.hasInstance. In questo caso, viene utilizzata la logica standard:obj instanceOf Classche controlla seClass.prototypeequivale ad uno dei prototype nella catena dei prototype diobj.In altre parole, li confronta tutti uno alla volta:
obj.__proto__ === Class.prototype? obj.__proto__.__proto__ === Class.prototype? obj.__proto__.__proto__.__proto__ === Class.prototype? ... // se una di questa true, allora viene ritornato true // altrimenti, una volta arrivati al termine della catena, ritorna falseNellesempio sopra
rabbit.__proto__ === Rabbit.prototype, quindi riceviamo immediatamente una risposta.In caso di ereditariet, il riscontro avverr al secondo passo:
class Animal {} class Rabbit extends Animal {} let rabbit = new Rabbit(); alert(rabbit instanceof Animal); // true // rabbit.__proto__ === Animal.prototype (no match) // rabbit.__proto__.__proto__ === Animal.prototype (match!)
Qui vediamo raffigurato cosa rabbit instanceof Animal confronta con Animal.prototype:
Comunque, abbiamo a disposizione anche il metodo objA.isPrototypeOf(objB), che ritorna true se objA si trova nella catena dei prototype di objB. Quindi la verifica obj instanceof Class pu essere riformulata come Class.prototype.isPrototypeOf(obj).
Un fatto divertente, che il costruttore stesso della Class, non viene coinvolto nella verifica! Solamente la catena dei prototype e Class.prototype vengono valutati.
Questo pu portare a diverse conseguenze quando la propriet prototype viene modificata dopo la creazione delloggetto.
Come nellesempio:
function Rabbit() {}
let rabbit = new Rabbit();
// modifichiamo il prototype
Rabbit.prototype = {};
// ...non p un rabbit!
alert( rabbit instanceof Rabbit ); // false
Bonus: Object.prototype.toString per il tipo
Sappiamo gi che gli oggetti semplici vengono convertiti a stringa come [object Object]:
let obj = {};
alert(obj); // [object Object]
alert(obj.toString()); // lo stesso
Questa la loro implementazione del metodo toString. Ma esiste una funzionalit nascosta che rende toString molto pi potente di cosi. Possiamo utilizzarlo come unestensione di typeof e come alternativa di instanceof.
Sembra strano? Lo ! Capiamo perch.
Secondo le specifiche, il metodo integrato toString pu essere estratto dalloggetto ed eseguito nel contesto di un qualsiasi altro valore. Ed il suo risultato dipende da quel valore.
- Per un numero, sar
[object Number] - Per un boolean, sar
[object Boolean] - Per
null:[object Null] - Per
undefined:[object Undefined] - Per gli array:
[object Array] - etc (personalizzabile).
Dimostriamolo:
// copiamo il metodo toString in una variabile per comodit
let objectToString = Object.prototype.toString;
// di che tipo questo?
let arr = [];
alert( objectToString.call(arr) ); // [object Array]
Qui abbiamo utilizzato call come descritto nel capitolo *Decorators* e forwarding, call/apply per eseguire la funzione objectToString nel contesto this=arr.
Internamente, lalgoritmo toString esamina this e ritorna il risultato corrispondente. Altri esempi:
let s = Object.prototype.toString;
alert( s.call(123) ); // [object Number]
alert( s.call(null) ); // [object Null]
alert( s.call(alert) ); // [object Function]
Symbol.toStringTag
Il comportamento di Object toString pu essere personalizzato utilizzando una propriet speciale delloggetto Symbol.toStringTag.
Ad esempio:
let user = {
[Symbol.toStringTag]: "User"
};
alert( {}.toString.call(user) ); // [object User]
Per molti oggetti specifici di un ambiente, esiste questa propriet. Qui vediamo alcuni esempi specifici per il browser:
// toStringTag per l'oggetto specifico d'ambiente:
alert( window[Symbol.toStringTag]); // Window
alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest
alert( {}.toString.call(window) ); // [object Window]
alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]
Come potete vedere, il risultato esattamente Symbol.toStringTag (se esiste), racchiuso in [object ...].
Al termine avremo typeof on steroids che non funziona solamente con i tipi di dato primitivo, ma anche con gli oggetti integrati, inoltre pu essere personalizzato.
Possiamo utilizzare {}.toString.call piuttosto di instanceof per gli oggetti integrati quando vogliamo ottenerne il tipo come stringa invece di effettuare una semplice verifica.
Riepilogo
Ricapitoliamo i metodi di verifica del tipi:
| funzionano con | ritorna | |
|---|---|---|
typeof |
primitivi | stringa |
{}.toString |
primitivi, oggetti integrati, oggetti con Symbol.toStringTag |
stringa |
instanceof |
oggetti | true/false |
Come possiamo vedere, {}.toString tecnicamente pi avanzato di typeof.
Invece loperatore instanceof funziona veramente bene quando lavoriamo con una classe e vogliamo controllarne lereditariet.
Commenti
<code>, per molte righe includile nel tag<pre>, per pi di 10 righe utilizza una sandbox (plnkr, jsbin, codepen)