Autor | Zpráva | ||
---|---|---|---|
juriad Profil |
#1 · Zasláno: 30. 10. 2014, 09:27:19
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() {}; Child.prototype = new Obj1(); Child.prototype.childFunc = function(){}; 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; 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(); 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 |
#2 · Zasláno: 31. 10. 2014, 06:56:43
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í Object u, 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 |
#4 · Zasláno: 31. 10. 2014, 08:57:44
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.constructor.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 |
#5 · Zasláno: 31. 10. 2014, 09:12:32
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 |
#7 · Zasláno: 31. 10. 2014, 09:45:15
_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 |
#10 · Zasláno: 1. 11. 2014, 20:43:45
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š.
|
||
Časová prodleva: 14 dní
|
|||
Filipkoss Profil |
#11 · Zasláno: 15. 11. 2014, 18:22:12
Nedostupnost
Object.create lze vyřešit polyfillem.
|
||
Časová prodleva: 9 let
|
0