« 1 2 »
Autor Zpráva
TomasJ
Profil
Ahoj. Pročetl jsem si na zdrojáku článek z roku 2010 o OOP v JS a udělal jsem si třídu. Chtěl bych vědět, jestli jsem ji navrhl správně? Děkuji.
<script>
var Scrollbar = function(min,max,value){
  this.min = min > 0 ? min : 0;
  this.max = max > 0 ? max : 0;
  this.value = value > 0 ? value : 0;
};

Scrollbar.prototype = {
  onScroll: function(){},
  scroll: function(value){
    this.value = value > this.max ? this.max : (value < this.min ? this.min : value);
    this.onScroll();
  }
};

var volumeScrollbar = new Scrollbar(0,100);
var otherScrollbar = new Scrollbar(0,20,10);
Scrollbar.prototype.onScroll = function(){
  alert("odscrollovano na " +this.value);
};
volumeScrollbar.onScroll = function(){
  alert("volume odscrollovano na " +this.value);
};
volumeScrollbar.scroll(100);
otherScrollbar.scroll(21);
</script>
Zatím to své využití nemá, ale bude mít. Jde mi hlavně o to kde přepisuji instanci volumeScrollbar metodu (spíš vlastní událost) onScroll. Je to v pořádku? Nebo se přepisuje metoda jinak, když chci, aby byla pozměněná jen v 1 instanci?
Radek9
Profil
TomasJ:
Ideálně by ještě možná bylo lepší nechat onScroll volné a při neexistující metodě to vůbec nevolat. Ale jinak je to v teoretické rovině asi správný návrh. Otázka je, jak se to potom použije v praxi.
TomasJ
Profil
Radek9:
Jak nejlépe otestuji, že metoda existuje?
Amunak
Profil
TomasJ:
Tohle snadno testuje existenci něčeho (objektu) a spoléhá to na to, že to je buď ta třída, kterou tam chceme, nebo nic.
if(Trida.metoda)
  alert('existuje');
Radek9
Profil
TomasJ:
Tedy přesněji takto:
Scrollbar.prototype = {
  scroll: function(value){
    this.value = value > this.max ? this.max : (value < this.min ? this.min : value);
    if (this.onScroll) {
      this.onScroll();
    }
  }
};
TomasJ
Profil
Jasný, díky. Já to přesně takto měl ještě před tím, než jsem to sem hodil. Ale nakonec jsem to změnil, protože jsem myslel, že to je "špatně".
Radek9
Profil
Radek9:
Těžko říct. Ale myslím, že ta prázdná funkce je špatně. Při tom volání to vytváří novou scope, nový arguments objekt atd., což se pak musí garbage collectorem odstraňovat. Takže jedna podmínka je proti tomu asi nic.
TomasJ
Profil
Ještě bych měl jeden dotaz. Není špatné, když při vytváření instance posílám konstruktoru jako parametr objekt s vlastnostmi? Názorný příklad:
var Scrollbar = function(properties){
  this.min = properties.min > 0 ? properties.min : 0;
  this.max = properties.max > 0 ? properties.max : 0;
  this.value = properties.value > 0 ? properties.value : 0;
  this.horizontal = properties.horizontal == true ? true : false;
  delete properties.min,properties.max,properties.value,properties.horizontal;
  
  this.properties = properties;
};
var volumeScrollbar = new Scrollbar({min:0,max:100,width:20});
preca1
Profil
TomasJ:
Mně zas přijde lepší volat prázdnou funkci. Z pohledu výkonu to vůbec nemá smysl řešit a volání funkce mi přijde čitelnější a kratší, než napsání podmníky. Navíc si tim zajistíš, že voláš metodu na objektu, kterej očekáváš. Je dobrým návykem programovat proti rozhraní; v JS nejsou rozhraní, takže se používá duck typing.

Pokud má konstruktor do 3 parametrů, klidně bych je vypsal. Při vícero bych určitě volil objekt.

Poznámky ke kódu:
K čemu je řádek 6 v [#8] a proč si na řádku 8 ukládáš referenci na properties, když jí (neměl bys) nepotřebuješ? Tohle sou mnohem horší věci (z logiky věci, výkonu i čitelnosti), než volání prázdný metody.
TomasJ
Profil
Zatím ji nepotřebuji, protože třída je prázdná, bez ostatních metod. Příkaz delete slouží k mazání proměnných (zde jsou to vlastnosti z objektu). A mažu je proto, aby zbytečně nebyly uložené v proměnné this.properties, když mají specifické proměnné dané (kvůli tomu, abych zaručil nastavení správné hodnoty - >=0). A ptám se jestli je to tak správně, či hodně špatně, případně jak to nahradit?
_es
Profil
TomasJ:
Prečo len nezmažeš 6. a 8. riadok?

A mažu je proto, aby zbytečně nebyly uložené v proměnné this.properties
Ak zmažeš 8. riadok, nebudeš musieť mazať ani to - sa to „zmaže“ samé.
preca1
Profil
Pleteš se. delete neslouží k mazání proměnných. Vyzkoušej si
var a = "test";
delete a;
console.log(a);
Slouží k mazání některých atributů a pokud nebudeš psát něco hodně složitýho, delete nebudeš nikdy potřebovat. Tohle použití je špatný a napáchá víc škody než užitky. JS engine si objekt properties nějakým způsobem optimalizoval. Použitím delete mu ho měníš pod rukama a deoptimalizuješ => stojí ho víc úsilí s ním pracovat.

Tím, že si uložíš proměnnou properties do ScrollBaru, znemožníš garbage collectoru její smazání, když už není potřeba. Tohle taky nedělej.
Abych to shrnul, řádky 6 a 8 smaž. Sou objektivně špatně.
TomasJ
Profil
_es:
Ak zmažeš 8. riadok,
No ale když smažu 8. řádek, nebudu mít uložené ostatní vlastnosti, které budou volitelné (šířka, výška, styl atp.) ne?

preca1:
Díky za objasnění funkce delete

Všem:
Jak tedy uložím properties do proměnné bez min, max, value a horizontal - správně?

Edit:
Nebo mám na ostatní vlastnosti použít novou metodu, ve které je budu postupně nastavovat?
preca1
Profil
No tak si i ty ostatní volitelné vlastnosti ulož. Stejně jako min, max, horizontal a value, ne?
TomasJ
Profil
preca1:
No, asi tedy ano, nevím proč to komplikuji. Stejně budu pracovat jen se známými vlastnostmi.
Každopádně díky za vysvětlení, proč je to špatně.
Radek9
Profil
preca1:
engine si objekt properties nějakým způsobem optimalizoval
To nemusí být nutně pravda. Enginy zpravidla optimalizují hlavně objekty tvořené konstruktory (pomocí skrytých tříd). Literály budou mít jen minimální optimalizaci. Naopak by tato optimalizace byla ztrátová, literály se hojně používají jako hashmapy.

TomasJ:
Jak tedy uložím properties do proměnné bez min, max, value a horizontal - správně?
Pokud zbytek údajů potřebuješ, pak přesně tak, jak jsi to napsal. Je zbytečné všechny z nich házet přímo na objekt.
1Pupik1989
Profil
[#5] Radek9: Viděl bych to podobně, akorát jinou podmínku.

Scrollbar.prototype = {
  scroll: function(value){
    this.value = value > this.max ? this.max : (value < this.min ? this.min : value);
    if (typeof this.onScroll === "function") {
      this.onScroll();
    }
  }
};

Je to o dost rychlejší.
preca1
Profil
1Pupik1989:
Je to o dost rychlejší.
O kolik?
joe
Profil
Já bych ten kód přepsal takto:
var Scrollbar = function (properties) {
    var options = $.extend(defaults, properties); // defaults jsou vychozi hodnoty
    this.properties = this.checkProperties(options);
};

Scrollbar.prototype.checkProperties = function (properties) {
    // kontrola vstupu
    return properties;
};

Nevím proč bych měl něco, co už někde mám, znovu přeukládat někam jinam. Přijde mi to zbytečné.
_es
Profil
joe:
Nevím proč bych měl něco, co už někde mám, znovu přeukládat někam jinam.
Čo ak sa namiesto volania
new Scrollbar({min:0,max:100,width:20})
zavolá funkcia ako
new Scrollbar(o)
a v premennej o bude objekt s príslušnými parametrami, ktorý sa následne zmení a použije na nové volanie new Scrollbar?
Chamurappi
Profil
Reaguji na 1Pupika1989:
Je to o dost rychlejší.
Zjistit datový typ a porovnat ho písmenko po písmenku s "function" je rychlejší, než převést hodnotu na boolean? Možné to asi je, chtěl bych vidět nějaký test.


Reaguji na joa:
Nevím proč bych měl něco, co už někde mám, znovu přeukládat někam jinam.
Zrovna tenhle týden jsem si podobnou úvahou vyrobil chybu — přesně tu, o které píše _es (v mém případě byl objekt o docela masivní flák dat využívaný na dvou místech).

Proč ten return this na řádku 3? Konstruktor vrací this automaticky, ne?
1Pupik1989
Profil
Tak jsem se mýlil. S rychlostí je takový mišmaš.

Mrkněte na to sami. http://jsperf.com/conditions-check
joe
Profil
_es & Chamurappi:
Omlouvám se, máte pravdu, nebyl jsem ve formě, ale už je to trochu lepší... nenapsal jsem ani to přiřazení hodnot správně, fakt bych si naplácal :-)

_es:
zavolá funkcia ako
new Scrollbar(o)new Scrollbar(o)
Pravda - přepíše se. Objekty, pole a snad už nic jiného je v JS předáváno referencí. Taky skoro pokaždé, pokud předávám takový parametr, tak tím rozšiřuji nějaký výchozí, ve zkratce tedy nějak takto:
var Scrollbar = function (properties) {
    this.properties = $.extend({ ... }, this.checkProperties(properties));
};
Díky za takové upozornění, asi se mi občas stane, že to tak napíšu a podobný problém si neuvědomím a může snadno dojít k problému...

Chamurappi:
Proč ten return this na řádku 3?
Vážně nevím, kde jsem k tomu přišel, že to tam pořád píšu a myslel jsem, že to tam opravdu musí být. Naopak to je úplně přebytečný řádek.

1Pupik1989:
Každý se někdy mýlí, ale osobně to tak píšu, protože co když by někdo do onScroll nepředal funkci? To už je otázka jak postupovat, jestli to nechat spadnou a nebo to přeskočit, protože někdo nečte (například) dokumentaci.
TomasJ
Profil
joe:
Já bych ten kód přepsal takto:
No už jen otázka: Kde jsi vzal $.extend(...)? Jelikož jsem začátečník v OOP a chci se to naučit pořádně (pure JS), odmítám hotové JS knihovny a podobné (mimochodem odsuzuji jQuery a nepoužívám ji).
Radek9
Profil
TomasJ:
$.extend by v zásadě mělo fungovat nějak takhle:
function extend(obj, ext) {
  for (var i in ext) if (Object.prototype.hasOwnProperty.call(ext, i)) {
    obj[i] = ext[i];
  }
  
  return obj;
}

odmítám hotové JS knihovny a podobné
To taky není nejlepší přístup. Zrovna na OOP je tu sice hafo knihoven a je těžké si vybrat, ale určitě bys našel nějakou, která by ti vyhovovala. Minimálně pro dedičnost, mixiny atp.

mimochodem odsuzuji jQuery a nepoužívám ji
Smím se zeptat na důvod? Mně se na jQuery nelíbí pouze přebytek obecných JS funkcí na hlavním objektu. Jinak jsem s jQuery jakožto s DOM knihovnou celkem spokojen.
TomasJ
Profil
Radek9:
Smím se zeptat na důvod?
Tak jednak to co jsi napsal i Ty. Další důvod je třeba ten, že jQuery zabírá dost místa a některé funkce JS jsou v ní jen přejmenovány. Jsem zastáncem čistého JS, ale nějaký extrémně vážný důvod k tomu nemám. JS se učím abych se ho naučil, pochopil principy. Ne abych stáhl jQuery a v ní to "odfláknul". Navíc jQuery není nejrychlejší. Když už, radši bych třeba mootools. Je podle mě lépe zpracovaná. Ale i tak ji nechci, raději si vše udělám sám a naučím se to, i když mi to zabere čas.
Chamurappi
Profil
Reaguji na TomaseJ:
Ve starším příspěvku jsem si všiml:
delete properties.min,properties.max,properties.value,properties.horizontal;
Čárka u delete na rozdíl od varu nemá speciální význam. Operátor delete má přednost před operátorem čárky, tudíž se smaže jen první ze zmíněných položek.


Reaguji na 1Pupika1989:
Zajímavé.
Ještě existuje jedna teoretická (rozhodně nedoporučitelná) možnost, která by měla být nejrychlejší v případě, kdy je velká pravděpodobnost, že this.onScroll existuje — zavolat this.onScroll() a chytat výjimku :-) … ovšem zachycení výjimky bude naopak výrazně pomalejší než jakákoliv podmínka. Bylo by to v určitých situacích mikrooptimalizační fuj.


Reaguji na joa:
myslel jsem, že to tam opravdu musí být. Naopak to je úplně přebytečný řádek.
Už jsem se bál, že mi uniká nějaká zásadní vlastnost konstruktorů :-)


Reaguji na Radka9:
Na ext.hasOwnProperty(i) se nedá spolehnout?
Je vůbec v tomto případě žádoucí odfiltrovat vlastnosti děděné z prototypu?

Zrovna na OOP je tu sice hafo knihoven a je těžké si vybrat, ale určitě bys našel nějakou, která by ti vyhovovala.
U OOP se ale hodí chápat, jak v JS funguje, což se řada knihoven snaží maskovat (simulují třídní dědičnost).
TomasJ
Profil
Chamurappi:
tudíž se smaže jen první ze zmíněných položek.
Zajímavé. Já si je vypisoval, a JS mi je hlásil všechny "smazané" jako undefined. Asi náhoda?
Radek9
Profil
TomasJ:
JS se učím abych se ho naučil, pochopil principy.
Ale na to pozor. DOM nerovná se JavaScript. A zrovna DOM je jedno z nejhůře navrhnutých API. jQuery to podle mě dostává aspoň do trochu zkousnutelné podoby. Samozřejmě, ztráta rychlosti tam je, ale ne extra výrazná. O velikosti knihovny bych se přel. Nemyslím si, že pro počítače dnešní doby představuje až takový problém.

Chamurappi:
Na ext.hasOwnProperty(i) se nedá spolehnout?
V případě, že je metoda přepsaná, tak ne. Asi většinou nenastane situace, kdy by se to stalo, ale stejně to radši píšu takhle.

Je vůbec v tomto případě žádoucí odfiltrovat vlastnosti děděné z prototypu?
Zrovna v tomto případě asi ne. Ale stejně jako u předchozího bodu, píšu to takhle, kdyby tam ta možnost v budoucnu byla.

U OOP se ale hodí chápat, jak v JS funguje, což se řada knihoven snaží maskovat (simulují třídní dědičnost).
To nepopírám. Nicméně minimálně na tu dědičnost se nějaká knihovnička hodí. Obecně ale vlastně moc nezáleží na tom, jaký způsob pro vytváření objektů se použije. Ve finále je z toho vždycky konstruktor, který se dá zase jinde použít jinak.
preca1
Profil
Radek9:
V případě, že je metoda přepsaná, tak ne.
??? Když někdo udělá Object.prototype.hasOwnProperty = "asdf", jak ti ten tvůj kód pomůže?
Píšeš to takhle i u metod u polí a řetězců? Jak řešíš, že někdo může přepsat isNaN?
« 1 2 »

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: