Autor Zpráva
juriad
Profil
Vycházím z terminologie: http://javascript.crockford.com/private.html. Vyžaduji kompatibility od IE8 výš, i když tuším, že v syntaxy velké změny nejsou.

Hledám nějaký mechanismus, jak v JS definovat pomocné metody pro konstrukci nějakého grafickeho prvku (konkrétně tabulky).
Kontruktor dostane parametr objekt options, podle kterých musí tabulku zkontruovat na zadaném místě.
Kontrukce spočívá v vytvoření DOMu tabulky a okolních ovládacích prvků (filtrování, paginace, checkboxy), dále vytvoření hlaviček (thead a tfoot) podle pokynů v options.
Dále se musí na jednotlivé prvky tabulky navěsit hromada posluchačů událostí řešící jednotlivé úkony.

Jelikož tabulka musí spolupracovat se zbytkem stránky, musí mít nějaké veřejné API:
- conductSearch()
- paginate()
- checkRows()
- refresh()

Moje otázka se týká rozvržení metod. Je mi jasné, že tyto obecné metody v API budu definovat v prototype.
Ale není mi jasné, kde a jak nejlépe definovat metody, které snad budou volány jen jednou a to konstruktorem při sestavování tabulky.
Samotné tyto metody zatím zabírají asi 600 řádek a mám je zatim definované jako priviledged (přiřazané kontruktorem do this.názevMetody; jedná se pozůstatek ze starého kódu, který předělávám).

Co bych chtěl od těchto metod:
1) aby nebyly součástí veřejného API tabulky,
2) aby fungoval this jako reference na kontruovanou tabulku při volání metody konstruktorem,
3) aby nezávislo na pořadí definic metod,
4) aby jimi nebyl přerušovaný výkonný kód konstruktoru (definice veřejných proměnných na začátku, volání pomocných metod na konci) a nebyl tedy roztahaný na několika set řádcích,
5) aby se trochu šetřila paměť, těch tabulek můžou být na stránce desítky.

Dále řeším, jak reprezentovat jiné pomocné metody - ty volané při realizaci veřejného API. Jedná se o metody, které zajištují komunikaci se serverem, výměnu obsahu tabulky, aktualizaci ovládacích prvků atp.
Od těchto metod bych chtěl to samé, jen je na ně ještě požadavek, že musí být dostupné z metod definovaných v prototypu.

Z toho, co jsem nastudoval mi vychází, že podle (1) musí být privátní, ale to se vylučuje s (2) a (4). O (3) popravdě moc nevím; vím jen že v JS moc nezáleží na místu definice proměnné kvůli vícefázovému parsování. Privátní metody nelze volat z verejných metod.

Napadá mě ještě jedno řešení: vytvořit si na začátku konstruktoru objekt, kterému do prototype naházím všechny pomocné metody. A tento objekt přiřadím do this.private.
Pomocné metody bych pak vždy volal pomocí this.private.jménoMetody. Nevýhodou je horší přístup k vnějšímu objektu tabulky pomocí nejspíš veřejné proměnné this.table vnitřního objektu. Také si nejsem jistý, jak je to s (5).

Existuje nějaký pěkný způsob řešení mého problému? Jak se to běžně řeší?

Do večera budu sledovat diskusi z mobilu, nic moc asi nenapíšu.
Chamurappi
Profil
Reaguji na juriada:
Viděl bych dvě možnosti…

(A) Mít zvlášť skutečný objekt se všemi metodami a zvlášť samotné pouzdro s veřejnými metodami. V konstruktoru pouzdra se lokálně vytvoří ten skutečný objekt a privilegované metody na pouzdře budou volat jeho metody. Paměťově to je celkem snesitelné, protože jediné, co se při vzniku každé instance duplikuje, jsou ty pouzdrové veřejné metody. Když to celé bude obalené v nějaké samovolajícíse funkci, může být ten skutečný objekt pro vnější kód neviditelný. Při tomto postupu jsou privátní i data skutečného objektu (zpřístupnit by je šlo jen pomocí setterů a getterů, které Explorer 8 nezná).
Zkusil jsem sestavit příklad kódu s tabulkou…

var Tabulka = (function()
{
  function Čokoláda(předvolby)  // toto je konstruktor skutečného objektu
  {
    this.kostičky = předvolby.kráva.vyždímat();
  }
  Čokoláda.prototype.ulomKostičku = function()
  {
    this.ulom(1);
  };
  Čokoláda.prototype.ulomŘádek = function()
  {
    this.ulom(4);
  };
  Čokoláda.prototype.ulom = function(n)
  {
    this.kostičky -= n;
  };
  
  return function(předvolby)  // toto je konstruktor pouzdra
  {
    var that = new Čokoláda(předvolby);
    this.ulomKostičku = function() { return that.ulomKostičku() };
    this.ulomŘádek = function() { return that.ulomŘádek() };
  };
})();

var m = new Tabulka({ kráva: Alpy.Fialová });
// objekt m má nyní dva členy: ulomKostičku a ulomŘádek

(B) Mít privátní metody jako úplně obyčejné funkce vedle těch veřejných-prototypových (opět v samovolajícíse funkci) a volat je z těch veřejných metod pomocí funkce.call(this, argument1, argument2, …). Při tomto postupu nejde mít privátní data přidružená k instanci, což může být podstatná nevýhoda oproti postupu (A).

var Tabulka;
(function()
{
  Tabulka = function(předvolby)
  {
    this.kostičky = předvolby.kráva.vyždímat();
  }
  Tabulka.prototype.ulomKostičku = function()
  {
    ulom.call(this, 1);
  };
  Tabulka.prototype.ulomŘádek = function()
  {
    ulom.call(this, 4);
  };
  function ulom(n)
  {
    this.kostičky -= n;
  }
})();

var m = new Tabulka({ kráva: Alpy.Fialová });
// objekt m má nyní tři členy: kostičky, ulomKostičku a ulomŘádek
juriad
Profil
Moc díky za obě možnosti a ukázky použití. Tušil jsem, že trik bude spočívat v zabalení do funkce, která objekt zkontruuje, ale na této úrovni s si JS ještě netykám.
Použiju první možnost. Mám však několik otázek:
1) Je bezpečné poznamenat si do property funkce, zda je veřejná a následně vygenerovat v pouzdru příslušné metody?
Podle mozilly jsou všechny metody a property široce podporované, ale možná něco přehlížím. Nevýhodou je netransparentnost signatury metody.
Cokolada.prototype.ulomKosticku.public = true;
...
var that = new Cokolada(predvolby);
var my = this;
for (var method in that) {
    (function(m) { // tento obal můžu přehodit dovnitř podmínky
        if (that[m].public === true) {
            my[m] = function() {
                return that[m].apply(that, arguments);
            };
        }
    })(method);
}

Další otázky jsem si vyřešil sám při snaze o jejich formulaci:
2) Je možné definovat veřejné instanční proměnné? Ano, stačí skutečnému objektu v konstruktoru předat referenci na pouzdro. Property pouzdra jsou veřejné, property skutečného objektu ne.
3) Je bezpečné volat v konstruktoru metody prototype (na 6. řádku zavolat this.ulom(2);)? Ano, metoda v prototype je definovaná dlouho předtím (v tomto příkladu) než se zavolá konstruktor. Property prototype se objektu nastaví při jeho alokaci těsně před zavoláním konstruktoru.
4) Jak se chová this a prototype když nepoužiju operátor new? This odkazuje na kontex volání funkce. Prototype funkci není dostupný.

Myslím, že teď už mám dostatečně silný nástroj k tomu, abych svou tabulku dokončil. Díky.

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:

0