Operátor instanceof umožÅuje ovÄÅit, zda objekt patÅà do urÄité tÅÃdy. Bere v úvahu i dÄdiÄnost.
Toto ovÄÅenà může být zapotÅebà v mnoha pÅÃpadech. NapÅÃklad může být použito k vytvoÅenà polymorfnà funkce, takové, která zacházà se svými argumenty různÄ v závislosti na jejich typu.
Operátor instanceof
Syntaxe je:
obj instanceof TÅÃda
Vracà true, jestliže obj patÅà do tÅÃdy TÅÃda nebo do tÅÃdy, která je z nà zdÄdÄna.
NapÅÃklad:
class KrálÃk {}
let králÃk = new KrálÃk();
// je to objekt tÅÃdy KrálÃk?
alert( králÃk instanceof KrálÃk ); // true
Funguje to i pro konstruktory:
// namÃsto tÅÃdy
function KrálÃk() {}
alert( new KrálÃk() instanceof KrálÃk ); // true
â¦A pro zabudované tÅÃdy jako Array:
let pole = [1, 2, 3];
alert( pole instanceof Array ); // true
alert( pole instanceof Object ); // true
ProsÃme vÅ¡imnÄte si, že pole patÅà i do tÅÃdy Object. Je to proto, že tÅÃda Array je prototypovÄ zdÄdÄna z tÅÃdy Object.
NormálnÄ instanceof pÅi ovÄÅovánà prozkoumává prototypový ÅetÄzec. Můžeme si vÅ¡ak také nastavit vlastnà logiku ve statické metodÄ Symbol.hasInstance.
Algoritmus operátoru obj instanceof TÅÃda funguje zhruba následovnÄ:
-
Pokud existuje statická metoda
Symbol.hasInstance, pak ji jen zavolá:TÅÃda[Symbol.hasInstance](obj). Ta by mÄla vrátit buÄtrue, nebofalse, a jsme hotovi. TÃmto způsobem si můžeme chovánÃinstanceofsami nastavit.NapÅÃklad:
// nastavÃme ovÄÅenà instanceOf tak, aby pÅedpokládalo, // že vÅ¡echno, co má vlastnost můžeŽrát, je zvÃÅe class ZvÃÅe { static [Symbol.hasInstance](obj) { if (obj.můžeŽrát) return true; } } let obj = { můžeŽrát: true }; alert(obj instanceof ZvÃÅe); // true: zavolá se ZvÃÅe[Symbol.hasInstance](obj) -
VÄtÅ¡ina tÅÃd nemá
Symbol.hasInstance. V tom pÅÃpadÄ je použita standardnà logika:obj instanceOf TÅÃdazjistÃ, zda seTÅÃda.prototyperovná nÄkterému z prototypů v prototypovém ÅetÄzci objektuobj.Jinými slovy, porovnává jeden po druhém:
obj.__proto__ === TÅÃda.prototype? obj.__proto__.__proto__ === TÅÃda.prototype? obj.__proto__.__proto__.__proto__ === TÅÃda.prototype? ... // je-li kterákoli odpovÄÄ true, vrátà true // jinak, pokud jsme dosáhli konce ÅetÄzce, vrátà falseV uvedeném pÅÃkladu
králÃk.__proto__ === KrálÃk.prototype, takže odpovÄÄ je vydána okamžitÄ.V pÅÃpadÄ dÄdiÄnosti bude shoda nalezena ve druhém kroku:
class ZvÃÅe {} class KrálÃk extends ZvÃÅe {} let králÃk = new KrálÃk(); alert(králÃk instanceof ZvÃÅe); // true // králÃk.__proto__ === ZvÃÅe.prototype (nenà shoda) // králÃk.__proto__.__proto__ === ZvÃÅe.prototype (shoda!)
Na tomto obrázku je vidÄt, co králÃk instanceof ZvÃÅe porovnává se ZvÃÅe.prototype:
Mimochodem, existuje také metoda objA.isPrototypeOf(objB), která vrátà true, jestliže se objA nacházà nÄkde v prototypovém ÅetÄzci objektu objB. Test obj instanceof TÅÃda tedy lze pÅepsat na TÅÃda.prototype.isPrototypeOf(obj).
Veselé je, že samotný konstruktor TÅÃda se na ovÄÅovánà nepodÃlÃ! Záležà jen na prototypovém ÅetÄzci a na TÅÃda.prototype.
To může vést k zajÃmavým důsledkům, když je vlastnost prototype zmÄnÄna po vytvoÅenà objektu.
NapÅÃklad zde:
function KrálÃk() {}
let králÃk = new KrálÃk();
// zmÄnÃme prototyp
KrálÃk.prototype = {};
// ...už to nenà králÃk!
alert( králÃk instanceof KrálÃk ); // false
Bonus: Object.prototype.toString pro typ
Už vÃme, že plané objekty se pÅevádÄjà na ÅetÄzec jako [object Object]:
let obj = {};
alert(obj); // [object Object]
alert(obj.toString()); // totéž
Taková je jejich implementace metody toString. Existuje vÅ¡ak skrytá vlastnost, která Äinà toString ve skuteÄnosti mnohem silnÄjÅ¡Ã. Můžeme ji použÃvat jako rozÅ¡ÃÅený typeof a alternativu pro instanceof.
Znà to zvláštnÄ? UrÄitÄ ano. Odhalme to.
Podle specifikace může být vestavÄný toString extrahován z objektu a spuÅ¡tÄn v kontextu jakékoli jiné hodnoty. A na oné hodnotÄ pak závisà jeho výsledek.
- Pro ÄÃslo to bude
[object Number] - Pro boolean to bude
[object Boolean] - Pro
null:[object Null] - Pro
undefined:[object Undefined] - Pro pole:
[object Array] - â¦atd. (nastavitelnÄ).
PÅedveÄme si to:
// pro pÅehlednost si zkopÃrujeme metodu toString do promÄnné
let metodaToString = Object.prototype.toString;
// jakého typu je tohle?
let pole = [];
alert( metodaToString.call(pole) ); // [object Array]
Zde jsme použili metodu call, popsanou v kapitole Dekorátory a pÅesmÄrovánÃ, call/apply, ke spuÅ¡tÄnà funkce metodaToString v kontextu this=pole.
VnitÅnÄ algoritmus metody toString prozkoumává this a vracà odpovÃdajÃcà výsledek. Dalšà pÅÃklady:
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
Chovánà metody toString můžeme nastavit pomocà speciálnà objektové vlastnosti Symbol.toStringTag.
NapÅÃklad:
let uživatel = {
[Symbol.toStringTag]: "Uživatel"
};
alert( {}.toString.call(uživatel) ); // [object Uživatel]
Tato vlastnost existuje u vÄtÅ¡iny objektů specifických pro urÄité prostÅedÃ. Uvedeme nÄkteré pÅÃklady specifické pro prohlÞeÄ:
// toStringTag v objektu a tÅÃdÄ specifické pro urÄité prostÅedÃ:
alert( window[Symbol.toStringTag]); // Window
alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest
alert( {}.toString.call(window) ); // [object Window]
alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]
Jak vidÃte, výsledkem je pÅesnÄ Symbol.toStringTag (pokud existuje), zabalený do [object ...].
Nakonec tedy máme âtypeof na steroidechâ, který funguje nejen pro primitivnà datové typy, ale i pro zabudované objekty a dokonce se dá nastavit.
Když tedy chceme u vestavÄných objektů zjistit název typu jako ÅetÄzec a ne ho jen ovÄÅit, můžeme mÃsto instanceof použÃvat {}.toString.call.
ShrnutÃ
ShrÅme si metody pro ovÄÅenà typu, které známe:
| funguje pro | vracà | |
|---|---|---|
typeof |
primitivy | ÅetÄzec |
{}.toString |
primitivy, vestavÄné objekty, objekty se Symbol.toStringTag |
ÅetÄzec |
instanceof |
objekty | true/false |
Jak vidÃme, {}.toString je technicky âpokroÄilejÅ¡Ãâ typeof.
A když pracujeme s tÅÃdnà hierarchià a chceme si ovÄÅit tÅÃdu, pÅiÄemž chceme vzÃt v úvahu dÄdiÄnost, opravdu se zaleskne operátor instanceof.
KomentáÅe
<code>, pro nÄkolik Åádků je obalte znaÄkou<pre>, pro vÃce než 10 Åádků vložte odkaz na pÃskoviÅ¡tÄ (plnkr, jsbin, codepenâ¦)