Operatoren instanceof tillader at tjekke om et objekt tilhrer en bestemt klasse. Den tager ogs arv i betragtning.
Sdan en tjek kan vre ndvendig i mange tilflde. For eksempel kan den bruges til at bygge en polymorfisk funktion, som behandler argumenter forskelligt afhngigt af deres type.
instanceof operatoren
Syntaksen er:
obj instanceof Class
Den returnerer true hvis obj tilhrer Class eller en klasse som arver fra den.
For eksempel:
class Rabbit {}
let rabbit = new Rabbit();
// er det et objekt der er skabt af Rabbit klassen?
alert( rabbit instanceof Rabbit ); // true
Det virker ogs med constructor funktioner:
// istedet for class
function Rabbit() {}
alert( new Rabbit() instanceof Rabbit ); // true
og med indbyggede klasser som Array:
let arr = [1, 2, 3];
alert( arr instanceof Array ); // true
alert( arr instanceof Object ); // true
Bemrk at arr ogs tilhrer Object-klassen. Det er fordi Array prototypisk arver fra Object.
Normalt undersger instanceof prototype-kden for tjekket. Vi kan ogs stte en tilpasset logik i den statiske metode Symbol.hasInstance.
Algoritmen for obj instanceof Class fungerer cirka sdan:
-
Hvis der er en statisk metode
Symbol.hasInstance, s kald den:Class[Symbol.hasInstance](obj). Den skal returnere ententrueellerfalse, og s er vi frdige. P den mde kan vi tilpasse adfrden forinstanceof.For eksempel:
// st et instanceof tjek der regner med // at alt med en canEat egenskab er et dyr class Animal { static [Symbol.hasInstance](obj) { if (obj.canEat) return true; } } let obj = { canEat: true }; alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) kaldes -
De fleste klasser har ikke
Symbol.hasInstance. I det tilflde bruges standardlogikken:obj instanceof Classtjekker omClass.prototypeer lig med en af prototyperne iobjs prototypekde.Med andre ord, sammenlign en efter en:
obj.__proto__ === Class.prototype? obj.__proto__.__proto__ === Class.prototype? obj.__proto__.__proto__.__proto__ === Class.prototype? ... // hvis en af svarene er true, returner true // ellers, hvis vi nede enden af kden, returner falseI eksemplet ovenfor
rabbit.__proto__ === Rabbit.prototype, s det giver svaret jeblikkeligt.I tilfldet af nedarvning vil matchningen vre p det andet trin:
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!)
Her er en illustration af hvadrabbit instanceof Animal sammenligner med Animal.prototype:
Forresten er der ogs en metode kaldet objA.isPrototypeOf(objB), der returnerer true hvis objA er et eller andet sted i kden af prototyper for objB. S testen af obj instanceof Class kan omformuleres som Class.prototype.isPrototypeOf(obj).
Det er lidt morsomt, men Class konstruktren deltager ikke selve i checket! Det er kun kden af prototyper og Class.prototype der har betydning.
Dette kan fre til interessante konsekvenser nr en prototype egenskab ndres efter objektet er oprettet.
Lige som her:
function Rabbit() {}
let rabbit = new Rabbit();
// ndret prototypen
Rabbit.prototype = {};
// ...ikke en "Rabbit" mere!
alert( rabbit instanceof Rabbit ); // false
Bonus: Object.prototype.toString for typen
Vi ved allerede at rene objekter er konverteret til streng som [object Object]:
let obj = {};
alert(obj); // [object Object]
alert(obj.toString()); // det samme: [object Object]
Det er deres implementering af toString. Men der er en skjult feature der faktisk gr toString mere kraftfuld end det. Vi kan bruge den som en udvidet typeof og et alternativ til instanceof.
Lyder det underligt? Ja, det gr! Men lad os afmystificere det.
I specifikationen str der, at den indbyggede toString kan trkkes fra objektet og udfres i konteksten af en anden vrdi. Og dens resultat afhnger af den vrdi.
- For et tal:
[object Number] - For en boolean:
[object Boolean] - For
null:[object Null] - For
undefined:[object Undefined] - For arrays:
[object Array] - etc (customizable).
Lad os demonstrere:
// kopier toString til en variabel for nemhedens skyld
let objectToString = Object.prototype.toString;
// hvad type er den?
let arr = [];
alert( objectToString.call(arr) ); // [object Array]
Her bruger vi call som beskrevet i kapitlet Decorators og videresendelse, call/apply til at udfre funktionen objectToString i konteksten this=arr.
Internt undersger toString algoritmen this og returnerer det tilsvarende resultat. Flere eksempler kunne vre:
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
The behavior of Object toString can be customized using a special object property Symbol.toStringTag.
For instance:
let user = {
[Symbol.toStringTag]: "User"
};
alert( {}.toString.call(user) ); // [object User]
For de fleste miljspecifikke objekter, er der en sdan egenskab. Her er nogle browser-specifikke eksempler:
// toStringTag til miljspecifikke objekter og klasser:
alert( window[Symbol.toStringTag]); // Window
alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest
alert( {}.toString.call(window) ); // [object Window]
alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]
Som du kan se, er resultatet prcis Symbol.toStringTag (hvis den eksisterer), omskrevet til [object ...].
I enden har vi typeof on steroids som ikke kun virker for primitive data typer, men ogs for indbyggede objekter og endda kan tilpasses.
Vi kan bruge {}.toString.call i stedet for instanceof for indbyggede objekter, nr vi vil have typen som en streng i stedet for blot at tjekke.
Opsummering
Lad os opsummere de type-tjekker, vi kender:
| virker for | returnerer | |
|---|---|---|
typeof |
primitiver | string |
{}.toString |
primitiver, indbyggede objekter, objekter med Symbol.toStringTag |
streng |
instanceof |
objekter | true/false |
Som vi kan se er {}.toString teknisk set en mere avanceret typeof.
Og instanceof operatoren kommer virkelig til sin ret nr vi arbejder med en klassehierarki og vil tjekke for klassen med hensyn til arv.
Kommentarer
<code>-taggen, for flere linjer - omslut dem i<pre>-tag, for mere end 10 linjer - brug en sandbox (plnkr, jsbin, codepen)