Vlastnost "prototype" je zeÅ¡iroka využÃvána samotným jádrem JavaScriptu. PoužÃvajà ji vÅ¡echny zabudované konstruktory.
Nejprve se podÃváme na detaily a pak na to, jak ji využÃt k pÅidánà nových schopnostà vestavÄným objektům.
Object.prototype
ÅeknÄme, že vypÃÅ¡eme prázdný objekt:
let obj = {};
alert( obj ); // "[object Object]" ?
Kde je kód, který vygeneruje ÅetÄzec "[object Object]"? Je to vestavÄná metoda toString, ale kde je? Objekt obj je prázdný!
â¦Krátká notace obj = {} je vÅ¡ak totéž jako obj = new Object(), kde Object je vestavÄný konstruktor objektu se svou vlastnà vlastnostà prototype, která odkazuje na obrovský objekt s metodou toString a jinými.
DÄje se následujÃcÃ:
Když je zavolán new Object() (nebo je vytvoÅen literálnà objekt {...}), jeho [[Prototype]] se nastavà na Object.prototype podle pravidla, které jsme probrali v pÅedchozà kapitole:
Když je tedy volána metoda obj.toString(), pÅevezme se z Object.prototype.
Můžeme si to ovÄÅit následovnÄ:
let obj = {};
alert(obj.__proto__ === Object.prototype); // true
alert(obj.toString === obj.__proto__.toString); //true
alert(obj.toString === Object.prototype.toString); //true
ProsÃme vÅ¡imnÄte si, že v uvedeném ÅetÄzci nenà nad Object.prototype žádný dalšà [[Prototype]]:
alert(Object.prototype.__proto__); // null
Jiné zabudované prototypy
Metody v prototypech si uchovávajà i jiné vestavÄné objekty, napÅ. Array, Date, Function a jiné.
NapÅÃklad když vytvoÅÃme pole [1, 2, 3], vnitÅnÄ se použije standardnà konstruktor new Array(). Takže jeho prototypem se stane a metody mu poskytne Array.prototype. To znaÄnÄ Å¡etÅà pamÄÅ¥.
Podle specifikace majà vÅ¡echny vestavÄné prototypy na svém vrcholu Object.prototype. Proto se také nÄkdy ÅÃká, že âvÅ¡echno je zdÄdÄno z objektůâ.
Zde je celkový pÅehled na obrázku (pro tÅi zabudované objekty, které se na nÄj vejdou):
OvÄÅme prototypy ruÄnÄ:
let pole = [1, 2, 3];
// dÄdà z Array.prototype?
alert( pole.__proto__ === Array.prototype ); // true
// pak z Object.prototype?
alert( pole.__proto__.__proto__ === Object.prototype ); // true
// a na vrcholu je null.
alert( pole.__proto__.__proto__.__proto__ ); // null
NÄkteré metody v prototypech se mohou pÅekrývat, napÅÃklad Array.prototype má svou vlastnà metodu toString, která vypÃÅ¡e prvky oddÄlené Äárkou:
let pole = [1, 2, 3]
alert(pole); // 1,2,3 <-- výsledek metody Array.prototype.toString
Jak jsme už vidÄli, Object.prototype má rovnÄž toString, ale Array.prototype je v ÅetÄzci blÞ, takže se použije varianta pro pole.
DÄdiÄnost zobrazujà i prohlÞeÄové nástroje, napÅÃklad vývojáÅská konzole v Chrome (pro vestavÄné objekty bude možná nutné použÃt console.dir):
Takto fungujà i jiné zabudované objekty, dokonce i funkce â to jsou objekty z vestavÄného konstruktoru Function a jejich metody (call/apply a jiné) se berou z Function.prototype. Také funkce majà svůj vlastnà toString.
function f() {}
alert(f.__proto__ == Function.prototype); // true
alert(f.__proto__.__proto__ == Object.prototype); // true, zdÄdÄna z objektů
Primitivy
NejzrádnÄjšà vÄc se dÄje s ÅetÄzci, ÄÃsly a booleany.
Jak si pamatujeme, nejsou to objekty. Pokud se vÅ¡ak pokusÃme pÅistoupit k jejich vlastnostem, vytvoÅà se pomocà vestavÄných konstruktorů String, Number a Boolean doÄasné obalové objekty, které poskytnou své metody a zmizÃ.
Tyto objekty se vytvoÅà pro nás neviditelnÄ a vÄtÅ¡ina motorů je vyoptimalizuje, ale pÅesnÄ tÃmto způsobem to popisuje specifikace. Metody tÄchto objektů rovnÄž pÅebývajà v prototypech, které jsou dostupné jako String.prototype, Number.prototype a Boolean.prototype.
null a undefined nemajà objektové obalySpeciálnà hodnoty null a undefined stojà stranou. Nemajà objektové obaly, takže metody a vlastnosti pro nÄ nejsou dostupné. A neexistujà ani odpovÃdajÃcà prototypy.
MÄnÄnà nativnÃch prototypů
Nativnà prototypy lze modifikovat. NapÅÃklad pÅidáme-li metodu do String.prototype, bude dostupná ve vÅ¡ech ÅetÄzcÃch:
String.prototype.zobraz = function() {
alert(this);
};
"BUM!".zobraz(); // BUM!
BÄhem procesu vývoje nás může napadnout, že bychom chtÄli mÃt i dalšà zabudované metody, a můžeme mÃt sklon pÅidat je do nativnÃch prototypů. To je vÅ¡ak obecnÄ Å¡patný nápad.
Prototypy jsou globálnÃ, takže je snadné způsobit konflikt. Jestliže dvÄ knihovny pÅidajà metodu String.prototype.zobraz, jedna z nich pÅepÃÅ¡e metodu druhé.
Modifikace nativnÃho prototypu je tedy obecnÄ považována za Å¡patný nápad.
V modernÃm programovánà existuje jen jeden pÅÃpad, kdy je modifikace nativnÃch prototypů vhodná. TÃm je polyfilling.
Polyfilling je pojem oznaÄujÃcà vytvoÅenà náhrady za metodu, která existuje ve specifikaci JavaScriptu, ale nÄkterý motor JavaScriptu ji jeÅ¡tÄ nepodporuje.
Pak ji můžeme implementovat ruÄnÄ a vsadit ji do zabudovaného prototypu.
NapÅÃklad:
if (!String.prototype.repeat) { // pokud žádná taková metoda nenÃ,
// pÅidáme ji do prototypu
String.prototype.repeat = function(n) {
// opakuje ÅetÄzec n-krát
// ve skuteÄnosti by kód mÄl být trochu složitÄjÅ¡Ã
// (úplný algoritmus je ve specifikaci),
// ale i nedokonalý polyfill se Äasto považuje za dost dobrý
return new Array(n + 1).join(this);
};
}
alert( "La".repeat(3) ); // LaLaLa
VypůjÄenà z prototypů
V kapitole Dekorátory a pÅesmÄrovánÃ, call/apply jsme hovoÅili o vypůjÄovánà metod.
To se dÄje tak, že vezmeme metodu z jednoho objektu a zkopÃrujeme ji do druhého.
NÄkteré metody z nativnÃch prototypů se vypůjÄujà Äasto.
NapÅÃklad jestliže vytváÅÃme objekt podobný poli, můžeme do nÄj chtÃt zkopÃrovat nÄkteré metody z Array.
NapÅÃklad:
let obj = {
0: "Ahoj",
1: "svÄte!",
length: 2,
};
obj.join = Array.prototype.join;
alert( obj.join(',') ); // Ahoj,svÄte!
Funguje to, protože vnitÅnà algoritmus vestavÄné metody join se zajÃmá jen o správné indexy a vlastnost length. Nekontroluje, zda objekt je skuteÄnÄ pole. Takto se chová mnoho zabudovaných metod.
Dalšà možnostà je použÃt dÄdiÄnost tak, že nastavÃme obj.__proto__ na Array.prototype, takže v obj budou automaticky dostupné vÅ¡echny metody Array.
To vÅ¡ak nenà možné, pokud již obj dÄdà z jiného objektu. Pamatujte, že můžeme dÄdit jen z jednoho objektu najednou.
VypůjÄovánà metod je flexibilnà a umožÅuje nám smÃchat funkcionality z různých objektů, jestliže je potÅebujeme.
ShrnutÃ
- VÅ¡echny vestavÄné objekty se chovajà podle stejného vzorce:
- Metody jsou uloženy v prototypu (
Array.prototype,Object.prototype,Date.prototype, atd.). - Samotný objekt si ukládá pouze data (prvky pole, vlastnosti objektu, datum).
- Metody jsou uloženy v prototypu (
- RovnÄž primitivy majà metody uložené v prototypech obalů:
Number.prototype,String.prototypeaBoolean.prototype. Obalové objekty nemajà pouzeundefinedanull. - VestavÄné prototypy lze modifikovat nebo do nich vkládat nové metody, ale mÄnit je se nedoporuÄuje. PravdÄpodobnÄ jediný pÅijatelný pÅÃpad je, když pÅidáváme nový standard, ale motor JavaScriptu ho jeÅ¡tÄ nepodporuje.
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â¦)