Autor Zpráva
juriad
Profil
Jen se chci ujistit, že to chápu správně. A že se to bude chovat tak, jak očekávám, bez překvapení. Pomiňme správnost návrhu, jde mi jen o průzkum toho, co mi JavaScript dovolí.
Dejme tomu, že mám:
var Obj1 = function() {};
Obj1.prototype.func = function() {return 1};

var Obj2 = function() {};
Obj2.prototype.func = function() {return 2};

A pak mám dítě:
var Child = function() {};
Pokud bych chtěl dítě podědit od Obj1, musím provést:
Child.prototype = new Obj1();
A pak si do toho prototypu můžu přidat další funkce:
Child.prototype.childFunc = function(){};
Následně ho můžu používat:
var c = new Child();
c.func();
c.childFunc();

Až potud je vše jasné.


Co když chci parenta nastavit později, až když je prototype hotový. Projde mi to vždy a všude? (Zkoušel jsem to ve Firefoxu a zdá se, že je to OK.)
Tedy:
var Child = function() {};
Child.prototype.childFunc = function() {return this.func();};

var oldProto = Child.prototype;
Child.prototype = new Obj1();
for (var m in oldProto) {
  Child.prototype[m] = oldProto[m];
}

new Child(); ...


A když už měníme prototype, šlo by mít každé dítě od různého rodiče?
var Child = function() {};
Child.prototype.childFunc = function() {};

var oldProto = Child.prototype;
Child.prototype = new Obj1();
for (var m in oldProto) {
  Child.prototype[m] = oldProto[m];
}

var c1 = new Child();

Child.prototype = new Obj2();
for (var m in oldProto) {
  Child.prototype[m] = oldProto[m];
}

var c2 = new Child();

var oldProto = Child.prototype;
Child.prototype = new Obj1();
for (var m in oldProto) {
  Child.prototype[m] = oldProto[m];
}

var c3 = new Child();

c1 a c3 mají stejného, ale současně různého rodiče (prototype je teď jiný objekt). Je v tom nějaký problém, nebo musím prototype recyklovat, abych nebyl ošklivě překvapený? (Vyjma dále zmíněného.)

A tady začínají problémy (http://kod.djpw.cz/dahb), netuším proč to alertuje 1, 2, 2.
Myslel jsem, že operátor new prostě skopíruje aktuální referenci na prototype.

Chápu správně, že jakmile objekt už existuje, tak jeho prototype už měnit nelze (ve smyslu změny rodiče, nikoli přidání metody)?
Občas jsem někde zahlédnul atribut constructor, je mi ve výše nastíněném kontextu k něčemu dobrý?
Také jsem někde zahlédl zmínku o tom, že přistupovat k prototype nijak jednoduše nelze, ale to se asi týkalo jen přístupu zevnitř existujícího objektu k jeho prototype.


Můžu vytvořit vypiplaný Obj1 a pak podobný Obj2, který Obj1 ukradne některé metody?
Obj2.prototype.func = Obj1.prototype.func;
Kam bude v takovém případě ukazovat this? Předpokládám, že na objekt, skrze jehož referenci/instanci/proměnnou byla ta metoda zavolána.

Co se bude dít, pokud bych poté ještě provedl úchylné:
Obj1.prototype = new Child();
kde aktuální Child.prototype míří na new Obj1()

Tuším správně, že dědičný cyklus ani kundičku v JS není možné (jakýmikoli prostředky vyrobit)?
Chamurappi
Profil
Reaguji na juriada:
Co když chci parenta nastavit později, až když je prototype hotový. Projde mi to vždy a všude?
Řekl bych, že ano. Přidáváš věci na instanci Obj1. V prototype je objekt jako každý jiný, kouzelné vlastnosti má až při práci s objekty vytvořenými konstruktorovou funkcí.

c1 a c3 mají stejného, ale současně různého rodiče
Na zdejším řádku 19 se do oldProto dává instance Obj2 doplněná o vlastnosti předchozího oldProto (přesněji o childFunc). Tudíž pak na řádku 22 do Child.prototype["func"] přiřadíš oldProto["func"] vracející dvojku, přepíšeš metodu, která byla na instanci Obj1 a vracela jedničku.

Chápu správně, že jakmile objekt už existuje, tak jeho prototype už měnit nelze (ve smyslu změny rodiče, nikoli přidání metody)?
Lze. Teprve v okamžiku, kdy zkoušíš číst z členské proměnné, se postupně prochází celý řetěz prototypů.
Řekneš-li si o c1.pokus, ale c1 žádný vlastní pokus nemá, tak se podíváme na Child.prototype, ten také nemá žádný vlastní pokus, ale je v něm instance Obj1, tak se podíváme na Obj1.prototype, tam také nic není, jen obecný objekt, který je instancí Objectu, tak se podíváme na Object.prototype, zase smůla, to je konečná => odpovíme ti undefined. Kdybychom cestou něco našli, dostal bys to.
Ale pokud něco přiřadíš do c1.pokus, bude to už jeho vlastní pokus, zde se dědičnost neuplatní. (To, jestli je člen vlastní nebo dědění, lze zjistit pomocí c1.hasOwnProperty("pokus").)

Občas jsem někde zahlédnul atribut constructor, je mi ve výše nastíněném kontextu k něčemu dobrý?
Asi není. Tím se jde dostat k funkci, která posloužila jako konstruktor. Teď mi není jasné, proč v c3.constructor je funkce Obj1… čekal jsem tam Child.

Také jsem někde zahlédl zmínku o tom, že přistupovat k prototype nijak jednoduše nelze
Možná souvisela s polointerní a méně podporovanou vlastností __proto__. Některým popisovatelům javascriptové dědičnosti připadá krkolomné popisovat prototypy přes prototype na konstruktoru.

Můžu vytvořit vypiplaný Obj1 a pak podobný Obj2, který Obj1 ukradne některé metody?
Ano.

Kam bude v takovém případě ukazovat this? Předpokládám, že na objekt, skrze jehož referenci/instanci/proměnnou byla ta metoda zavolána.
Přesně tak.

Tuším správně, že dědičný cyklus ani kundičku v JS není možné (jakýmikoli prostředky vyrobit)?
Řekl bych, že vytvořit jde, ale zřejmě ho nejde použít způsobem, který by vyvolal chybu nebo způsobil nějaký jiný problém. On vlastně ten závěr prototypového řetězu končí ve smyčce na Object.prototype.
Radek9
Profil
Chamurappi:
Lze. Teprve v okamžiku, kdy zkoušíš číst z členské proměnné, se postupně prochází celý řetěz prototypů.
To sice ano, ale prochází se rovnou řetězec prototypů, nikoli konstruktorů a jejich vlastností prototype. Tím pádem jde prototype již vytvořeného objektu změnit jen pomocí jeho vlastnosti __proto__, nebo funkcí Object.setPrototypeOf. Pokud ale vyměním prototype konstruktoru za jiný, na již vytvořené instanci se to neprojeví.

juriad:
A tady začínají problémy ( Živá ukázka), netuším proč to alertuje 1, 2, 2.
Protože v tom tvém for-in cyklu kopíruješ i zděděné metody toho prototypu. V první fázi (po dědění z Obj1) vypadá Child.prototype nějak takto:
childFunc
func -> Obj1#func

Po dědění z Obj2 takto:
childFunc
func -> Obj2#func // Neboť jsi nepřepsal oldProto

Po opětovném dědění z Obj1:
childFunc
func // Tady už je vlastní a odpovídá Obj2#func, protože jsi ji zkopíroval z předchozího prototypu

Pokud bys potom napsal delete Child.prototype.func;, opět by správně ukazovala na Obj1#func.
_es
Profil
Chamurappi:
„Také jsem někde zahlédl zmínku o tom, že přistupovat k prototype nijak jednoduše nelze“
Možná souvisela s polointerní a méně podporovanou vlastností __proto__.
V ECMAScript 5 sa dá použiť Object.getPrototypeOf(). V ES3 sa dá použiť objekt.construc­tor.prototype, no to nemusí správne fungovať, ak sa po vytvorení objektu jeho prototyp nejako nevhodne zmenil (alebo vlastnosť constructor).

Radek9:
nebo funkcí Object.setPrototypeOf
Až na to, že je tá funkcia akosi málo podporovaná: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf#Browser_compatibility.
juriad
Profil
Chamurappi:
Díky za vysvětlení.

Radek9, Chamurappi:
Chyba byla na tom 19. řádku. Nevšiml jsem si, že proměnnou oldProto přepisuji. Prostě jsem kopíroval a zapomněl to smazat.

Myslím, že je mi to o něco jasnější. Nejdivnější mi vlastně příšlo, že předka určuji až když už mám definovaný kontruktor. A jak je vidět, můžu to přesunout až před okamžik zavolání new.

Šlo mi o to napsat si funkci inherit, které předám dvojici: konstruktor a konstruktorPředkaNeboNull. A ta funkce mi vrátí nový něco, na co můžu zavolat new a dostanu instanci „konstruktoru“. Nebylo mi jasné, v který okamžik je vhodné pracovat s prototype.
Radek9
Profil
_es:
Až na to, že je tá funkcia akosi málo podporovaná
Já přece netvrdím, že není. Nezmiňoval jsem to, protože to je na té stránce hned nahoře.

juriad:
Nebylo mi jasné, v který okamžik je vhodné pracovat s prototype.
Ideálně prostě hned po vytvoření konstruktoru. Moje funkce inherit vypadá nějak takto:
var inherit = function (Constructor, Parent) {
  var Empty = function () { this.constructor = Constructor; };
  Empty.prototype = Parent.prototype;
  
  Constructor.prototype = new Empty(); // Tady se dá použít i Object.create, ale tam je taky problém s podporou
};

Dědičnost potom vypadá takhle:
var Parent = function () {};

Parent.prototype.a = function () { return 1; };

var Child = function () {};
inherit(Child, Parent);

Child.prototype.b = function () { return 2; };

var obj = new Child();
obj.a(); // 1
obj.b(); // 2
juriad
Profil
_es:
OK, kompatibilitu řeším, ale teď mi jde o princip.

Radek9:
Hm, nechápu, proč tam vytváříš Empty. To co děláš zaručí, že se nebude volat constructor Parenta, ale jeho metody budou dostupné potomkovi (ale instanční proměnné Parenta nebudou existovat vůbec; tedy metody Parenta musí být pure - nesmí záviset ani měnit samotný objekt).

Proč nepoužiješ prosté:
var inherit = function (Constructor, Parent) {
  Constructor.prototype = new Parent();
};

Vždy jsem používal jen operátor new. K čemu by mi mohl být Object.create()?
Radek9
Profil
juriad:
ale instanční proměnné Parenta nebudou existovat vůbec; tedy metody Parenta musí být pure - nesmí záviset ani měnit samotný objekt
Pokud by ses spoléhal na to, že všechny instanční položky z rodiče ti budou stačit z prototypu, mohl bys docela narazit. V případě, že ty instanční vlastnosti budou jiné pro každou instanci (jako že asi budou), tak je musíš vytvářet v konstruktoru a to pomocí zavolání toho rodičovského konstruktoru:

var Parent = function (a) {
  this.a = a;
};

…

var Child = function (a, b) {
  Parent.call(this, a);
  
  this.b = b;
};

V opačném případě bys měl v prototypu u a undefined pro každou instanci Child.

Vždy jsem používal jen operátor new. K čemu by mi mohl být Object.create()?
Object.create slouží k vytvoření instance přímo z objektu (prototypu) bez volání konstruktoru. Takhle by vypadala zjednodušená funkce inherit:
Constructor.prototype = Object.create(Parent.prototype);
Constructor.prototype.constructor = Constructor;
juriad
Profil
Radek9:
Zatím jsem předpokládal, že mi bude stačit mít všechny instanční proměné private - tedy nikdy nebudu potřebovat k nim přistupovat z potomka.

Ale teď koukám, že já potřebuji constructoru předka předat argumenty a to je důvod (na který jsem už zapomněl), proč jsem chtěl pozdržet nastavení prototypu až do volání new. Bohužel to by mi také nefungovalo, neboť (http://kod.djpw.cz/pahb) instance Obj se stane základem prototype Child. (A instanční proměnná a tedy bude členem prototype).

Teď už to chápu. To Parent.call(this, a); je fakt fíkané. Už také chápu, proč ti stačilo zpřístupnit jen metody skrze instanci Empty. On vlastně objekt Parenta nikde neexistuje, jeho metody jsou v Empty a data jsou přímo v potomkovi.

Díky moc, zase jsem se něco naučil. Metody call jsem dříve nepotřeboval použít.
1Pupik1989
Profil
Radkův kód je správně, akorát bych constructor klidně dal do prototype. Přiřazení prototypu k prázdné funkci je správně, jelikož by původní funkce mohla odchytávat chyby při chybějícím parametru. Prázdnou funkcí se tomu vyhneš.
Filipkoss
Profil
Nedostupnost Object.create lze vyřešit polyfillem.

Vaše odpověď

Mohlo by se hodit

Neumíte-li správně určit příčinu chyby, vkládejte odkazy na živé ukázky.
Užíváte-li nějakou cizí knihovnu, ukažte odpovídajícím, kde jste ji vzali.

Užitečné odkazy:

Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: