Třídy, dědičnost a OOP v Javascriptu – I

Jak funguje objektově orientované programování v Javascriptu? Má Javascript třídy nebo nemá? Jak se implementuje dědičnost? Na tyto otázky si odpovíme v sérii článků, a ukážeme si, že Javascript je flexibilní, objektově orientovaný jazyk, vhodný nejen pro rychlé prototypování, ale i pro vývoj složitých aplikací.
Seriál: OOP v Javascriptu (3 díly)
- Třídy, dědičnost a OOP v Javascriptu – I 15. 3. 2010
- Třídy, dědičnost a OOP v Javascriptu – II 22. 3. 2010
- Třídy, dědičnost a OOP v Javascriptu – III 29. 3. 2010
Nálepky:
Začneme stručným shrnutím a sjednocením pojmů, kterým se v našem výkladu
nevyhneme, a poznámkami k textu.
Pokud vás nějaký aspekt jazyka zaskočí, nebo pokud se vám
bude zdát, že jsem něčemu nevěnoval dostatečnou pozornost, doporučuji odkaz: https://developer.mozilla.org/en/JavaScript, který by si měl uložit každý, kdo to s Javascriptem myslí vážně.
Na všechny příklady budeme používat službu jsFiddle, díky které si můžete příklady rovnou naživo vyzkoušet.
Českou
terminologii použiji, pouze existuje-li ustálený český ekvivalent.
Nesouhlasím s nutností překladu do češtiny za každou cenu, ba co víc,
domnívám se, že je to činnost zbytečná a škodlivá. Bez znalosti
angličtiny se programátor stejně neobejde.
Ačkoliv budu psát
o dědičnosti a objektově orientovaném programování, zmíním i
funkcionální prvky jazyka. Martin Malý mi kdysi položil otázku: Je
nějaký důvod, proč by lidi v JavaScriptu měli programovat především „objektově“ a
ne „funkcionálně“? Tato otázka klade falešné dilema. Nejlepší je znát a
využívat oba přístupy, a ty rozhodně nejsou zaměnitelné, spíše se
doplňují. Doufám, že na konci minisérie bude zřejmé jak. Ukážeme si
všechny obvyklé způsoby, jak vytvořit „třídu“, a to od nejjednoduššího po
nejsprávnější.
Tak má ten Javascript třídy, nebo nemá?
Jak by řekl sir
Humphrey, ano i ne. Javascript nemá klasické třídy. Avšak podle
definice „A class is a construct that is used as a blueprint (or
template) to create objects of that class.“ (in: http://en.wikipedia.org/wiki/Class_(computer_science)) třídy
má. Mezi programátory Javascriptu pak platí konsensus, že za třídu
považujeme konstrukční funkci, krátce konstruktor, který využívá
vlastnosti prototype. Jak se taková konstrukční funkce s vlastností
prototype liší od klasické třídy, si povíme v průběhu článku.
Přehled pojmů
Scope a closure
Upozornění:
Podrobně se tématu věnoval Petr Staníček ve svém seriálu. Já jej zde
opakuji pro osvěžení a také proto, že další úhel pohledu nikdy není na
škodu.
Scope je rozsah viditelnosti proměnné. Scope
může být globální nebo lokální. Globální scope je jeden, a v prohlížeči
jej vždy reprezentuje objekt window. Lokální scope generuje v
Javascriptu pouze funkce. Scope si lze představit jako mercedes s
tmavými skly. Zevnitř je vidět ven, ale zvenčí není vidět dovnitř.
Javascript umožňuje do sebe funkce zanořovat. Tím se tvoří scope chain. Tady už příklad trochu kulhá, ale dejme tomu, že zaparkujeme mercedes v garáži, která má okna také ztmavená. Viz příklad Scope.
Closure je
pouze jiný název pro scope. Když hovoříme o closure nějaké funkce,
hovoříme o scope, ve kterém byla funkce deklarována. Proč je vnější
scope funkce tak důležitý, že si zasluhuje vlastní název? Je to proto,
že je na něj zevnitř funkce vidět, ať už funkci voláme z jakéhokoliv
místa v programu. To je velmi užitečné, protože v Javascriptu jsou
funkce prvotřídní objekty.
To zhruba znamená, že si je můžeme ukládat do proměnných nebo předávat
argumentem, čili zacházet s nimi jako s objekty. Funkce si vždy nese
odkaz na scope, ve kterém byla deklarována. Zanořování funkcí a to, že
každá funkce má vlastní closure, jsou funkcionální prvky jazyka
Javascript.Viz názorný příklad Closure.
Objekt
V Javascriptu platí, že vše co není primitivní typ je asociativní pole,
krátce objekt. Funkce je objekt, pole je objekt, i regulární výraz je
objekt. Pro začátečníky bývá matoucí, že vše je objekt, tedy
asociativní pole, a přesto existuje samostatný typ object. Jak je to
možné? Je to jednoduché: Object je v hierarchii všech typů nejvýše, je
tedy předkem pro všechny ostatní typy, díky čemuž všichni jeho potomci
dědí jeho vlastnosti. Objekt je zkrátka v Javascriptu vše, co umožňuje
přiřadit vlastnost, a předává se referencí.
Funkce, metoda, konstruktor, třída
Vše, co je zmíněno
v titulku, je v Javascriptu stále a jedna tatáž funkce. Proč jí tedy
nazýváme čtyřmi jmény? Protože pojmenování určuje roli, kterou funkce
hraje. V Javascriptu funkce slouží k více účelům.
Funkce, to je prostá definice. Není přiřazena k žádné třídě, k žádnému objektu, a podle konvence se píše v camelCase, tedy s malým písmenem na začátku.
var foo = function () {};
Pokud je funkce přiřazena nějakému objektu, říkáme jí Metoda. Rovněž ji píšeme v camelCase.
user.foo();
Konstruktor, neboli konstrukční funkce, je určena k vytváření instancí. Proto se jí také říká třída. Třída a konstruktor znamená v Javascriptu to samé. Píšeme ji v PascalCase, tedy s velkým písmenem na začátku, které nám naznačí, že bychom měli použít operátor new.
var Person = function() {}; var joe = new Person();
this, kontext
Klíčové slovo this odkazuje na kontext. Kontext je objekt, ve kterém funkci voláme. V následujícím příkladu vidíme, že voláme-li funkci bane přímo, je kontextem globální objekt window. Přiřadíme-li funkci objektu user, stane se kontextem user. Jak tečkový operátor přesně funguje, si povíme později.
var bane = function() { this.banned = true; }; bane(); // true alert(window.banned); var user = {}; user.bane = bane; user.bane(); // true alert(user.banned);
Příklad: http://jsfiddle.net/Nz9TJ/
Jak
je vidět, funkce můžeme volat nad různými objekty. Většinou hovoříme o
kontextu, ve kterém je funkce volána. Kontext můžeme funkci i vnutit,
pomocí klíčových slov call a apply, jak ukazuje následující příklad: http://jsfiddle.net/KnaWr/
Techniky vytváření tříd
Ukážeme
si dvě falešné a jednu správnou. Falešné, protože ve skutečnosti nejde
o vytváření instancí tříd, ale o konstrukci podobných objektů. Jaký je
v tom rozdíl, nám postupně osvětlí příklady.
Zneužití closure
// zajímavý, avšak špatný způsob vytváření "instancí" v Javascriptu var Animal = function(p_name) { var name = p_name; return { showName: function() { alert(name); } } }; var kitty = Animal('Kitty'); // alert 'Kitty' kitty.showName(); // false alert(kitty instanceof Animal);
Příklad: http://jsfiddle.net/4CXkY/
Popíšeme si, co vidíme. Funkce Animal přijímá parametr p_name, který si ukládá do lokální proměnné name. Následně vrací objekt s metodou showName, která vidí lokální proměnnou name
ve svém closure. Na dalším řádku vytvářím „instanci“ kitty
. Všimněte
si, bez použití operátoru new. Ten je v tomto případě zcela zbytečný,
objekt, který funkce Animal vrací, si vytvářím sám. Na posledním řádku
vidíme využití „instance“ v praxi. Každá instance je unikátní, každá má
vlastní scope (v něm je uložena proměnná name
). Proměnná name
je
zapouzdřena, protože je lokální, nelze ji změnit odjinud, než z vnitřku
funkce Animal.
„Hurá!, to bylo jednoduché. Takhle
jednoduše, že se dělají třídy? V tom případě mám hotovo, ještě napsat
testy, podědit a… a sakra, co když budu mít privátní metodu, jak ji
otestuji? Nijak, no nic. Teď musím ještě vytvořit „třídu“ Cat, potomka
třídy Animal… hmm, jenže jak? Možná, že kdybych…“
Stop,
takhle ne! Výše uvedený příklad ilustruje pružnost Javascriptu, ale
rozhodně není správným způsobem, jak tvořit třídy. Ukázali jsme si jej proto, že naznačuje obvyklý způsob, jakým se v Javascriptu imitují
privátní proměnné, totiž pomocí lokálních proměnných schovaných v
closure.
„Privátní“ proměnné v Javascriptu – má to smysl?
Každý javascriptový programátor by si měl uvědomit, že snaha ultimátně zapouzdřit nějakou proměnou, či rovnou celou funkcionalitu, je většinou marná. Javascript je dynamický jazyk.
Pokud do své aplikace pustíme cizí kód, o kterém nevíme, co dělá, a
proto raději „zapouzdřujeme“, trpíme falešným pocitem bezpečí. Smiřme
se s faktem, že Javascript není vhodný jazyk pro psaní software na
ovládání jaderných elektráren, a zkusme to brát jako jeho výhodu.
Pokud
v Javascriptu něco „zapouzdřujeme“, děláme to hlavně proto, abychom
čtenáři kódu naznačili: „Tohle je privátní, tak si toho nevšímej.
Nespoléhej, že tahle metoda bude vždy fungovat stejně, možná v příští
verzi nebude fungovat vůbec.“ Mírně to naznačíme podtržítkem v názvu,
brutálně pomocí closure. Jestli někde má smysl simulovat privátní
proměnné pomocí closure, tak jedině u statických objektů, tedy modulů.
Moduly
Modul v Javascriptu rovná se statický objekt. Nelze vytvářet jeho instance. Implementace snad ani nemůže být jednodušší.
var console = { log: function(message) { // ... nějaký kód } };
Douglas Crockford „vymyslel“ vlastní verzi modulu, která má, považte, privátní lokální proměnné.
var console = (function() { var iAmPrivate = 'foo'; return { log: function(message) { // ... nějaký kód } } })();
Popišme
si kód: Vytváříme anonymní funkci, kterou okamžitě voláme
(ty kulaté závorky na konci). Vnitřní scope anonymní funkce je closure
metody log
. Metoda log
je v objektu, který vracíme, a který se ukládá
do proměnné console
. Je nemožné z vnějšku změnit proměnnou iAmPrivate
,
zato je velmi jednoduché přepsat objektu console
metodu log
. Proto je
hra na „privátní“ proměnné v Javascriptu převážně čas mařící manýrou. Přesto se tato technika občas používá, a to ze dvou rozumných důvodů:
- potřebujeme referenční proměnné, a nechceme špinit globální scope
- mikrooptimalizace
jQuery
je knihovna, která se maximálně vyhýbá znečištění globálního scope.
Fakticky má pouze dvě globálně viditelné proměnné, jQuery a $. Dolar
si však jako globální magickou über funkci vybralo více knihoven.
Proto jQuery navrhuje vlastní kód zapouzdřit takto:
(function($) { /* some code that uses $ */ })(jQuery);
Začátečník
(a, co si budeme namlouvat, i profesionál), je rád, že namísto dlouhého j
Q u e r y, může všude psát sexy dolary, aniž by riskoval konflikt s
jinou knihovnou.
Druhým, a jen zřídka rozumným, důvodem jsou mikrooptimalizace (premature optimization is the root of all evil).
(function() { var EventType = SomeNamespace.InnerNamespace.ClassName.EventType; })();
Jak
lze vidět, vytváříme si lokální referenci na EventType nějaké třídy.
Kdykoliv budeme enumeraci EventType potřebovat, odkážeme se na lokální
proměnnou. Javascript tak nebude vyhodnocovat x tečkových operátorů
stále dokola. Toto má smysl, pokud chceme „zpřehlednit“ kód, a také,
pokud nám záleží na tom, aby funkce využívající EventType, byla zhruba
o tisícinu milisekundy rychlejší. Někdy to smysl má, protože Internet
Explorer. Ale pouze pro výkonnostně kritické funkce, například $type ($type
je funkce pro detekci všech možných typů, se kterými se můžeme v
Javascriptu setkat). K modulům se ještě vrátíme, až budeme probírat
mixování.
„Vylepšené“ třídy
Následující příklad už vypadá lépe. Je tam operátor new (ten za nás vytváří objekt), používá se this
(tím se na vytvořený objekt odkazujeme uvnitř metody), operátor instanceof funguje. Je to technika navržená Douglasem Crockfordem. Douglas si dokonce vytvořil vlastní názvosloví: metodě showName
se říká privilegovaná, protože ačkoli je veřejná, má přístup k privátní lokální proměnné name
.
Douglas Crockford udělal pro svět Javascriptu hodně, ale některé
články, věnované OOP a dědičnosti, se mu zrovna nepovedly. Ani tento
způsob není správný:
var Animal = function(p_name) { var name = p_name; // privilegovaná metoda this.showName = function() { alert(name); } }; var kitty = new Animal('Kitty'); kitty.showName(); // 'Kitty' alert(kitty instanceof Animal); // true
Předchozí
příklad „zneužití closure“ i tento mají společné, že ukazují „konečně
ten správný“ postup, jak mít v Javascriptu privátní členy. A oba jsou
špatné. Privátní členy můžeme akceptovat u modulů, ale tvořit třídy
tímto způsobem nelze. Vzdejte to. Funkcionální prvky Javascriptu mají
své využití jinde. Možná vám vrtá hlavou, proč jsou předchozí techniky
špatné. Každá nakonec selže na jednom z těchto bodů:
privátnílokální proměnné a metody neotestujete- closure a privilegované metody se pro každou instanci vytváří zas a znova, což je nemalá (a hlavně zbytečná) zátěž
- nefunguje operátor instanceof
- veškerá legrace skončí, až se pokusíte podědit takovou „třídu“
- v potomku nelze volat metodu rodiče
- již existující instanci nelze (elegantně) přidat nebo změnit vlastnost
Konec první části
Ukázali jsme si několik metod, jak v Javascriptu vytvářet objekty, představili jsme si jejich výhody a nevýhody, řekli si, kde se používají, a především – proč jsou špatné. V příští části se podíváme na „konečně správné“ řešení pomocí prototype.
Nepřehlédněte!
Autor článku Daniel Steigerwald vystoupí s přednáškou na téma Třídy, dědičnost a OOP v Javascriptu na letošní konferenci Internet Developer Forum 2010. Přijďte si jej (a samosebou i další přednášející) poslechnout a zeptat se jich na to, co vás zajímá, ve středu 7. dubna do Národní technické knihovny (registrace nutná).
léta působím jako javascriptový programátor a napsal jsem už mnoho článků o javascriptu a musím vám napsat, že jste to s tím vším nějak popletli.
Odjakživa funkce byla objektem, z funkce jde vytvářet další objekty, pomocí prototypové dědičnosti lze objekty rozšiřovat. Veškeré objekty vycházejí z objektu Object a z něj vycházejí další vlastnosti, které jsou všem objektům stejné.
Doporučil bych vám tímto pročíst si pár knih o OOP v Javascriptu, nebo si alespoň přečíst mé články na http://programovani.blog.zive.cz/category/javascript/
Mám tam spoustu článků o OOP v Javascriptu a také mimochodem pětidílný seriál o funkcích v Javascriptu.
Rozhodně za tři díly neopíšete celé OOP Javascriptu to ani náhodou. Tento článek je velmi nepovedený
Díky za komentář, kritiku vždy vítám. Avšak odříznu-li z vašeho komentáře úvod, reklamu na váš vlastní seriál, a dojem z mého, jediné co zbude je druhý odstavec, a ten je skoro totožný s mým textem. Čili, odkud jste vyčetl, že funkce není objekt?
PS: zkusmo jsem se na na jeden díl vašeho seriálu podíval (http://programovani.blog.zive.cz/2009/08/javascriptova-vlastnost-prototype-3/)
1) Nikdy nedeklarujte metodu v konstruktoru! (proč se dozvíte v druhém díle)
2) Nikdy neprocházejte pole pomocí cyklu for in!!
3) propertyIsEnumerable je nespolehlivá metoda, nemá žádný smysl ji používat
Som velmi rad, ze som nemusel to co pisete autorovi napisat ja, lebo osadenstvo zdrojaku by si zasa myslelo, ze kritizujem iba z principu.
Plne suhlasim. Clanok je popleteny a matuci. Autor zrejme moc nechape OOP ale najma Javascript ako jazyk.
Vsimli ste si, ze vsetci diskutujujuci, ktoru maju skutocne dobre IT vedomosti maju na zdrojaku auru okolo 10. :)
Super, takový komentář jsem si vždy přál :) Schválně, vypíchněte tři největší chyby v mém článku. Nemůžu se dočkat! :-)
O to bych si moc neříkal. Já je sem zatím nepsal, protože článek i přes to má svou informační hodnotu :-)
Danieli, odkaz na mé články o Javascriptu nejsou žádnou reklamou, pouze jsem Vás odkazoval na článek kde je proveden správný teoretický popis, i když to může vypadat namyšleně, já reklamu nepotřebuji, denně mi na blog chodí 150 až 700 návštěvníků. Také mé výtky neberte jako nějaké naparování či vychloubání se, tak to vůbec nemyslím.
Chcete abychom Vám řekli o nedostatcích ve Vašem článku, takže za prvé, píšete: „Funkce, to je prostá definice. Není přiřazena k žádné třídě, k žádnému objektu“,
že funkce jení objektem, je chyba, funkce je objektem, přebírá některé své „vrozené“ vlastnosti z objektu Object,tudíž je přiřezena k objektu Object v Javascriptu existuje dokonce objekt jako Function , z funkce tedy můžeme vytvářet objekty.
Objekty totiž můžeme vytvářet buďto tím, že vytvoříme funkci například takto:
function funkce(){}
a tímto zápisem se obracíme na konstruktor funkce var x=new funkce();
tečkovou syntaxí se pak můžeme obracet na jednotlivé proměnné funkce, objekt také můžeme vytvářet tak, že ho vytvoříme jako asociované pole. Ovšem vytvoření objektu jako asociované pole má svá omezení. Takový objekt můžeme nazývat literálním objektem.
Tudíž tvrdit, že objekt je v Javascriptu asociované pole je špané. Dříve se objekty opravdu vytvářeli jako asociovaná pole, dnes se dají vytvořit i z funkce a je to častější způsob vytvoření. Dále pokud vytvoříte objekt z asociovaného pole tak nemůžete vytvářet novou instanci pomocí slova new.
Dále píšete, nahoře, že funkce není objekt a dole zase že je.
Dále píšete o Closure, ale všude se closure říká uzávěry. Dále se ještě jednou vrátím k objektům. Dále když už hovoříte o objektech bylo by velmi vítané kdybyste řekl, že existují vestavěné objekty, a dále že objekty si můžeme vytvořit vlastní například z funkcí nebo jako asociované pole.
Celý popis funkce máte zmatečný, za prvé z funkce můžeme vytvářet objekt, funkce obsahuje proměnné a metody a můžeme ji nazývat třídou, na třídu se můžeme odkazovat konstruktorem pomocí slůvka new. Dále objekt window je globálním nejvyšším objektem vzhledem k DOM stromu dokumentu, nikoli k samotnému Javascriptu.
Dále uvedené názvosloví je velice špatné, psát jako o špatné funkci je opravdu špatný způsob, měl byste psát o uzávěrách. Dále píšete o modulech, ve skutečnosti jde o prázdný objekt, že kterého nejde vytvářet další instance takto se skutečně dají vytvářet statické objekty.
Opět jste špatně pochopil anonymní funkce. závorky před a za anonymní funkcí se dělají kvůli poli působnosti dané funkce, kvůli ničemu jinému se nedělají.
Dále pokud se nepletu je jQuery totéž co $, a navíc se tyto proměnné dají přepsat na vlastní označení.
Prostě celý článek je nepřehledný, nedůsledný, špatné názvy, špatně jste pochopil OOP v Javascriptu, jsou zde neúplné věci……
Dále, proč bych nemohl deklarovat metodu v konstruktoru, pokud chci tuto metodu ihned zavolat tak ji tam můžu volat, nemusím ji psát jinam. Dále co se týče for in tak to skutečně nepoužívám, je to tam uvedeno pouze jako možný způsob.
Zkuste si ten článek přečíst ještě jednou. Daniel zde za ‚funkci‘ označuje něco co je samostatná funkce nevázaná na žádný scope (vyjma globálního) a nevolá se jako ‚konstruktor‘. Jde pouze o vysvětlení použité terminologie a proč pro jednu věc používá několik názvů.
BTW Asociované pole != asociativní pole.
Clanok je maximalne terminologicky paskvil. Nevhodny pre zaciatocnika, lebo mu nic neda a iba ho poletie. Daniel v clanku nevysvetlil ani ako funguje operator new (mozno to sam nevie) a uz tvori objekty ako divy.
OOP v JS není pro začátečníky a ani v článku nikde není nijak naznačeno, že je pro začátečníky určen.
Osobně netuším, jaké projekty za sebou mají IT odborníci peter a olin, ale Danovu práci znám a vím, že on jen málo lidí má takové znalosti JavaScriptu jako on. Tak si prosím od cesty nechce poznámky typu „možno ani nevie ako operator new funguje“.
> Danovu práci znám a vím, že on jen málo lidí má takové znalosti JavaScriptu jako on.
Ak je toto pravda, optom sa asi dano nevie vyjadrovat primerane svojim vedomostiam. :(
Celé to tvrzení je mylné.
Podívejte se na následující:
function funkce(){}
document.write(typeof(funkce));
výsledek bude function
document.write(funkce.constructor);
výsledek bude Function() { [native code] }
Z tohoto jasně vyplývá, že funkce je objektem typu function, konstruktorem funkce je Function() a Function vyplývá z objektu Object.
Ovšem jiné je, když napíše, že funkci lze volat bez konstruktoru ale pokud vytvoří funkci z objektu Function tak lze funkci volat i konstruktorem.
Pane, výrokem
Opět jste špatně pochopil anonymní funkce. závorky před a za anonymní funkcí se dělají kvůli poli působnosti dané funkce, kvůli ničemu jinému se nedělají.
jste vaší připomínku moc neozřejmil. Když vy můžete zkratkovitě soudit, budu i já. Pokud takto zmatkovitě píšete články, tak bůh s vašimi čtenáři.
O jaké závorky vlastně jde? :)
jde přesně o toto:
(functio(){
tělo funkce
})
ty kulaté závorky na začátku a na konci jsou zde kvůli poli působnosti
Můžete mi to vysvětlit? Co je podle vás pole působnosti? Ve článku se mluví o scope, je scope to co myslíte? Není náhodou scope omezen složenými závorkami, které ohraničují tělo funkce?
Velmi rád vysvětlím.
Pole působnosti neboli anglicky Scope je trošku složitější téma než tady David nastínil.
Prakticky by se dalo říct, že pole působnosti je dáno ve funkci složenými závorkami. Pole působnosti také například řeší kdy daná funkce má přístup k proměnným další funkce.
Přístup k proměnným z jedné do druhé funkce mohou velmi krásně usnadnit uzávěry.
Nechci být opět podezříván z reklamy, ale o poli působnosti a uzávěrách si můžete přečíst v mém článku zde: http://programovani.blog.zive.cz/2009/07/rozsah-pusobnosti-v-javascriptu-a-uzavery/
Právě díky poli působnosti se doporučuje anonymní funkce uzavírat do kulatých závorek:
(function(){})
Ty kulaté závorky ale nemají s polem působnosti nic společného. Z anonymní funkce se zde vytvoří objekt, který se potom dalším párem závorek zavolá.
Přesně tak, kulaté závorky na to nemají vliv.
Jmenuji se Dan, David je můj bratr ;). Nechci vás zklamat, ale to co píšete je hrubý omyl. Scope (pole působnosti, viditelnosti.. vida jak jsou ty překlady zrádné ;) generuje v Javasciptu pouze funkce. Mluvit o složených závorkách je zavádějící, generuje snad literální zápis objektu nějaký scope?
Podívejte se na tento kód:
Podstatné jsou ty kulaté závorky na konci (nebo operátor new ve třetím příkladě) Všechny příklady vytváří scope. Scope vytváří v Javascriptu funkce (nic jiného), a to pouze tak, že je funkce zavolána.
Jak vidíte, v prvním příkladu kulaté závorky nejsou, a stejně funguje. Nicméně, důrazně doporučuji závorky vždy psát, a to čistě proto, že takový kód je pak čitelnější. Už na začátku je vidět, že je funkce uzavřena do závorek, takže se s ní chystám asi něco dělat. Třetí příklad zmiňuji jen pro zajímavost, používá jej třeba Dean Edwards.
Pokud však funkci ničemu nepřiřazuji, tedy klasické:
Kulaté závorky kol funkce, jsou skutečně nutné, avšak pouze kvůli syntaxi. Samy o sobě scope nevytvářejí, ani funkci nevolají.
Chyba.
[code]var x = (function() {return ‚x‘;})();[/code]
žádný scope nevytvoří, jen předá návratovou hodnotu funkce obsaženou uvnitř, zde to je string ‚x‘
Jde to udělat 50ti způsoby jako třeba…
[code]
function foo() {return ‚x‘;}
var x = (foo)(); // x
var foo = function() {return ‚x‘;}
var x = (foo)(); // x
var x = ((((((function() {return ‚x‘;}))))()));
[/code]
pokaždé bude v proměnné x string ‚x‘, něco jiného je ovšem tohle:
[code]
function foo() {this.test = ‚x‘;return ‚x‘;}
var x = (new foo); // x
var foo = new function() {this.test = ‚x‘;return ‚x‘;}
var x = (foo); // x
var x = ((((((new function() {this.test = ‚x‘;return ‚x‘;}))))));
[/code]
kde žádný string nedostaneme, nýbrž objekt, v každém případě. Závorky tam jsou jen na okrasu, abys věděl, že tohle opravdu na scope nemá vliv.
Mno, kouknul jsem se na vaše stránky a na nejnovější článek. Tedy rozhraní v jazyku, který nemá typovou kontrolu je opravdu užitečná, ale fakt užitečná věc :-)).
Jste si jistý, že víte o čem mluvíte?
Děkuji za kritiku :),
narozdíl od Daniela mám všechny články logické, vykládající v souvislosti. O všem co tady Daniel píče tak o tom mám také články. Skutečně jeho článek je matoucí, nic neříkající. Například ta etapa s moduly, to co tam nazývá moduly jsou prázdné objekty.
Pokud o tom nevíte, tak v Javascriptu se dají nasimulovat kroky a postupy OOP, které nejsou původně v Javascriptu vytvořeny. Jedním z nich jsou jmenné prostory které ve skutečnosti jmennými prostory nejsou. Rozhraní jsou možná v Javascriptu „šílenějším“ nápadem, ale i tak se používají, první díl celkem třídílného seriálu začíná od bezvýznamného vytvoření rozhraní z komentáře, kde kontrola není možná až po rozhraní kde kontrola možná je.
Ve svých článcích se snažím lidem otevřít nový náhled na Javascript.
Opravdu vím o čem píši mám za sebou ve světě internetu bohatou zkušenost v oboru Javascriptu.
Nikoho své články nenutím číst. Pokud se vám nelíbí tak je nečtěte.
Nic ve zlém, ale mě Danielův článek přijde docela pochopitelný i když nějaké nepřesnosti v něm jsou. Díky „pružnosti“ javascriptu a tomu, že není skutečně OO je ovšem i terminologie kolem něho dost pružná. Např. modul a prázdný objekt mě přijde jako stejný termínový problém jako třeba funkce a metoda.
Ale k těm rozhraním, simulovat jmenné prostory v javascriptu má určitě smysl, protože pak omezím možnost konfliktů názvů, ale k čemu mě bude rozhraní?
I pokud naimplementuje vlastní kontrolu, jestli objekt odpovídá rozhraní, tak se chyba odhalí až při běhu scriptu. Takže je to ve skutečnosti test, který se spouští při každém spustění scriptu. To mě nepřijde moc efektvní. Testy se běžně dělají jenom po změnách v programu (vím že např. v článku zmiňovaný Douglas Crockford podobné postupy taky popisuje, ale o jejich užitečnosti mě zatím nepřesvědčil).
Ale možná mě něco uniká, mohl by jste prosím vás uvést odkaz na nějakou javascriptovou knihovnu nebo framwork, kde se rozhraní používá?
Myslim ze resenim by bylo pouzivat jiny jazyk, ktery umi rozhrani (na rozdil od javascriptu) :-)
Jo, třeba Javu díky GWT.
Python?
Vy jste si ten článek prostě nepřečetl poctivě, a většinu z toho co mi vyčítáte sem nikde nenapsal. Vezmeme to popořadě, a snad se nám společně podaří najít shodu.
K druhému odstavci. Už vím, z čeho pramení vaše výtka. Vy totiž nevíte, co to znamená slovo přiřadit, anglicky assignment. Když píši „funkce není přiřazena“, rozhodně tím nemám na mysli, že funkce není potomkem Object. Je to jasné?
Ke třetímu odstavci. Opakujete pouze co sem psal. Mimochodem, kdybyste článek nečetl tak ledabyle, věděl byste, že pokud voláme funkci s operátorem new, nikdy ji nepíšeme s malým písmenem na začátku (viz váš příklad). Operátorem new totiž voláme funkci, která je konstruktorem, a konstruktory se píší vždy s velkým písmenem na začátku.
Čtvrtý odstavec. Objekt je v Javascriptu vždy asociativní pole, i kdyby jste se na hlavu stavěl. Ukažte mi jediný příklad, kdy v Javascriptu není objekt asociativním polem. Neukážete, protože žádný takový příklad neexistuje. Řekl bych, že jste si pojem asociativní pole spletl s pojmem object literal.
„Dále píšete, nahoře, že funkce není objekt a dole zase že je.“
Opět, neznáte význam slova přiřadit. Nikde sem nepsal, že funkce není objekt.
„Dále píšete o Closure, ale všude se closure říká uzávěry.“
Proč nepoužívám český pojem uzávěr, sem zmínil v úvodu. Není zažitý, a podle mého ani přesný.
Nativním (vestavěným) objektům se věnuji v dalších dílech. Nepovažuji za nutné, správné, a ani možné, zmínit v prvním díle úplně vše.
„Celý popis funkce máte zmatečný,“
Já funkce nikde nepopisuji, takže nevím co máte namysli zmatečným popisem. Pouze osvětluji, kdy se funkci jak říká, protože to je pro následující text podstatné. Dále, Objekt a modul je to samé. Jestli je, nebo není prázdný, nehraje roli.
Co se týká závorek. Vy prostě neumíte číst. Cituji sebe: „ty kulaté závorky na konci“. Co na tom nechápete? Ještě jednou: na konci. Nikde není napsáno: „Na obou koncích funkce“.
K jQuery. Vždyť právě v tom svém příkladu ukazuji, jako použít closure anonymní funkce, aby bylo možné použít bezpečně $. Nechápu, co vám na tom příkladu vadí.
No a závěrem, odpověď na vaši otázku: „Proč bych nemohl deklarovat metodu v konstruktoru“, naleznete v dalším díle. To, že si tak moc zkušený programátor, jakým bezesporu jste (a nemyslím to zle), tuto otázku vůbec klade, je mi důkazem, že mělo smysl článek napsat.
Ke třetímu odstavci. Opakujete pouze co sem psal. Mimochodem, kdybyste článek nečetl tak ledabyle, věděl byste, že pokud voláme funkci s operátorem new, nikdy ji nepíšeme s malým písmenem na začátku (viz váš příklad). Operátorem new totiž voláme funkci, která je konstruktorem, a konstruktory se píší vždy s velkým písmenem na začátku.
V popisu javascriptu, na ktery odkazujete na zacatku clanku, se mala pismena vesele uzivaji, viz https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Creating_New_Objects/Using_a_Constructor_Function
No vida, mají to tam špatně. Nic není dokonalé :) O co se tedy opírá mé tvrzení? Jednak je to samotná implementace Javascriptu ve všech prohlížečích. Všechny konstrukční funkce jsou psány s velkým písmenem na začátku (Array, Object, Function, Number, Boolean atd.), stejně tak statické objekty (Math).
S malým písmenem na začátku se naopak píší všechny metody: (apply, call, toString, join, push, atd.), stejně tak všechny instance: (document, body, window, location).
Z toho také vycházejí všichni autoři Javascriptových knihoven (yui, dojo, mootools, jquery…)
Kde naopak úzus chybí, je psaní namespace. YUI preferuje PascalCase (Y.Object), Google closure vždy camelCase (goog.object).
Důvod, proč konstruktory píšeme s velkým písmenem však nespočívá pouze v tom, že to tak dělají všichni, ale také proto, že nám velké písmeno naznačuje, že bychom měli použít operátor new.
Jeden článek, v jinak velmi dobré referenční dokumentaci, na tom nic nezmění.
Zkrátka jde o konvenci ne o rys jazyka. :)
Ano, je to tak – constructor function jedině s velkým písmenkem na začátku! ;-)
No dobre, jde tedy ale jen o konvenci. Syntaxe je spravna i s malymi pismeny. Z vaseho popisu to vyznivalo tak, ze mala pismena jsou uplne spatne, error.
Porušování konvencí je mnohdy horší než error. Zatím co error se dá většinou dohledat a opravit, tak nedodržování konvencí je na přeshubu.
No jistěže sem mluvil o konvenci :) Správná syntaxe, tedy funkční a spustitelný kód, může vypadat třeba i takhle: (pro pobavení) http://jsfiddle.net/VnC9v/
Puvodni clanek mi prijde o dost srozumitelnejsi nez vase reakce :)
Ahoj Dane, pěkný článek, už se těším na další díly. Doufám, že to ostatním pomůže prorazit do JS stejně jako mně tvoje přednáška před lety. :)
Mám jen jednu výtku. Stále dokola opakuješ, že privátní/lokální členy nejdou testovat. Pokud budu považovat testování = jednotkové testování, pak je tato vlastnost zcela žádoucí. Testování privátních členů je test smell. :) Privátní členy netestujeme, protože jejich chování se může měnit – ostatně proto jsou privátní. Funkčnost privátních členů testujeme pomocí chování objektu navenek. Pokud je nedokážeme v testech veřejného rozhraní otestovat, pravděpodobně jde o zbytečné členy… :)
Jojo, vidím to stejně :)
Díky Aleši za komentář, tuhle výtku sem popravdě čekal :) Ano, máš pravdu, privátní členy bychom testovat neměli, v ideálním světě. Sám ale píšeš: „Sice to je ukázka toho, že něco smrdí, ale stejně se to občas může hodit“. To je i můj názor. Kdyby článek byl vyčerpávají, musel bych dodat, neotestujete, protože Javascript žádnou reflexi na lokální členy nemá. Je jasné, že dříve nebo později se s nutností otestovat privátní členy setkáme. Prostě proto, že testy psané proti veřejnému API, by byly příliš vyčerpávající, nebo nepřesné. Pokud máš třídu chlap, s veřejnou metodou sbalHolku, a ta veřejná metoda volá privátní metody: vykoupej se, nauč se Gutha Jarkovského, atd., tak je rychlejší, přesnější a snadnější otestovat jednotlivé kroky, než slepě celou metodu sbalHolku. Kdybys ty metody schoval v closure, už je nikdy neotestuješ. Nicméně, testování zdaleka není jediným důvodem, jak ukáže druhý díl článku.
Ano, psal jsem před dvěma a půl lety. :) Navíc u metod vykoupej se a nauč se Gutha Jarkovského není žádný důvod ke skrývání, tyto mohou být klidně veřejně vystaveny a tudíž jdou snadno otestovat.
Nevim, proc by mely byt verejne vystaveny, kdyz nemusi. Jen kvuli testovani? Zvlastni pristup…
Páč asi se nebudete mýt pouze v případě že budete balit ženu, že? Stejně tak platí i druhý případ. U těchto metod není žádný důvod je skrývat a s testováním to nemá nic splečného. On je to totiž nešťastně zvolení příklad. Zbal ženu by bylo lepší implementovat jako Command pattern s injektovanou strategií. ;)
To, že testy poněkud ovlivňují veřejné API, je běžné. Někdo to považuje za přirozené, někdo za špatné, ale změna API kvůli testům rozhodně není nic neobvyklého.
Daniel jistě ví o čem píše, ale bohužel mi článek přijde trošku blbě strukturovaný :( Např. dva způsoby implementace tříd proložené povídáním o „modulech“.
Btw. kód:
var foo = function () {};
je opravdu neštastný, vzhledem k ladění. Tím se totiž vytváří anonymní funkce, která se přiřazuje do proměnné foo. Takže při ladění ve stack-trace uvidíte něco jako „anonymous_function125“ (záleží na konkrétní implementaci JS).
Troufnu si tvrdit, že je lepší funkce pojmenovávat, tedy psát:
var foo = function foo () {};
Ale Javascript je tak bohatý jazyk s rozmanitými možnosti, jak zapsat jednu věc, že IMHO nejde napsat článek o JS, na kterém by si někdo něco „špatného“ nenašel ;-) Kolikrát i záleží na kontextu, ve kterém autor JS uvažuje (použití v prohlížeči, na serveru, v databázi, …).
Nicméně se těším na další díly :) A přednášku na IDF :)
Naprosto souhlasím s to nešťastnou strukturou.
A nechávat si rozuzlení třídy na další díl, je také nevhodné. Pokud jsou uvedeny špatné příklady, měl by být hned uveden ten správný.
Víte… ono to rozuzlení zabíralo v celém textu (cca 45.000 znaků) dobrou třetinu. Přemýšleli jsme s autorem, že bychom ho – právě kvůli takovým komentářům – vydali celý v jednom kuse, ale tak dlouhý text většina čtenářů „neučte“. Jediná možnost, jak nechat „správný“ postup v jednom článku se „špatným“, bylo jejich společné vydání (30.000 znaků). Jakýmkoli způsobem bráno se text vždy logicky rozpadal na „jak se to dělá a dělat nemá“, „jak se to dělat má a proč“ a „jak se to dá udělat elegantně“. Nakonec jsme usoudili, že čtenář ví, co je to „seriál“, a chápe, že ačkoli je každý díl logicky uzavřen, bude něco řečeno až příště…
1. Seriál nemá být psán stylem „tady máte jeden díl, ale zatím ho nečtěte, protože bez toho dalšího v tom budete mít akorát bordel“. Tento díl v žádném případě není „logicky uzavřen“. Spíše než odbornou publikaci váš seriál zatím připomína mexickou telenovelu – tak dnes jsme se nic nedozvěděli, ale v příštím díle se už Esmeralda určitě vdá!
2. Buď jste měli oba (špatný i správný) postupy publikovat v jediném díle, nebo jste měli ten správný publikovat jako první. Psychologové už dávno vědí, že v člověku zůstávají hlavně první dojmy. Můžete do článku stokrát psát, že „takhle se to dělat nemá“, ale protože tu chybí jakékoliv srovnání (a jak se to tedy dělat má?), povedlo se vám v řadě lidí „zakotvit“ vámi uvedené „nesprávné“ postupy jako ty jediné správné (protože žádné jiné jste v článku neuvedl). To je síla podvědomí.
3. Javascript neumím a ani se ho neplánuji učit. Článek jsem četl jen proto, že mně zajímá, jak jsou jednotlivé metodiky (zde OOP) implementovány v různých jazycích. Nemohu proto posoudit obsahovou správnost článku, ale pouze formální. A po formální stránce je to tedy opravdu průšvih.
Zde jen kratka noticka: uvedeny zapis „var A = function B() {}“ je principialne spravny napad, ovsem v Internet Exploreru (6–8) timto vzdy vznika v globalnim objektu vlastnost B, nezavisle na hodnote A.
Tedy, pokud treba pisu:
MujNamespace.Trida.prototype.Metoda = function Trida_Metoda() {}
tak v IE bude najednou existovat window.Trida_Metoda.
Opravím vás. Vlastnost B vzniká v aktuálním, ne globálním scope. Pokud tedy váš příklad zabalíte do vlastního scope, pomocí anonymní funkce například, globální scope zůstane netknut. Jinak nevýhody takového zápisu shrnuji v odpovědi na jiný komentář.
Ano, máte pravdu, jedná se o aktuální scope. Nezávisle na tom však stále platí upozornění, že Internet Explorer (resp. JScript) se v tomto směru chová jinak, než ostatní implementace (nebál bych se dokonce použít termín „špatně“).
Nakonec jsem si dovolil lehce upravit kus vašeho kódu, linkovaný z příspěvku http://zdrojak.root.cz/clanky/oop-v-javascriptu-i/nazory/7955/ (vyhodnocování shora dolů) – moje verze viz http://jsfiddle.net/zsBLH/. Je vidět, že i pojmenovaná funkce není ve scopu viditelná až do doby své deklarace – to proto, že vznikla jako „function expression“ a ne jako „function declaration“. Hezké počtení o této problematice viz https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Functions#Function_constructor_vs._function_declaration_vs._function_expression nebo http://yura.thinkweb2.com/named-function-expressions/.
Blbě strukturovaný.., asi ano ;) Možná by na začátku každého článku měl být obsah. Z něj by bylo snad patrné, že odbočka k modulům svou logiku má. Mluvím o hře na privátní členy, proto zmiňuji, že své využití mají pouze mimo dědičnost a třídy. Tedy pouze u modulů nebo globálních funkcí.
K tvé připomínce. Ač je logická a správná, myslím, že vyvozuješ nedobrý závěr. Zkusme si porovnat klady a zápory obou způsobů:
Zápis, který považuji za jediný správný, tedy anonymní funkce přiřazená do proměnné foo, skutečně způsobí, že žádný ladící nástroj (Visual Studio, Firebug, Chrome debugger atd.) nemůže zobrazit název funkce. Kde by jej také vzal, že :)
Avšak každý ladící nástroj, nás po kliku na řádek anonymous_function přenese na deklaraci funkce. Osobně se domnívám, že se s tím dá obstojně žít (a to jsou některé mé projekty velmi složité :)
Nyní k záporům. První co mi vadí, je duplikace názvů. Dříve nebo později, bude třeba kód refaktorovat, a myslet na to, že je třeba vždy měnit název na dvou místech, mi přijde otravné (můj názor). Co mi přijde horší, je rozdílný způsob vyhodnocování viditelnosti takto deklarované funkce (mimochodem, není pravda co se píše v jiném komentáři zde, že funkce se stane globální).
Kód se vyhodnocuje „odshora dolu“. Až dojde Javascript k var, proměnná foo se stane viditelná v celém svém scope. Ovšem než se Javascript pustí do samotného vyhodnocování, mrkne se, jestli v celém scope není nějaká funkce přímo pojmenována, a pokud ji nalezne, „zviditelní“ ji ihned.
Nejlépe to osvětlí příklad: http://jsfiddle.net/Y7eRB/
Nebudu argumentovat oblíbeným hypotetickým případem „něco si někde přepíšeme“, který je málo pravděpodobný, pokud zrovna nepíšeme program stylem plachta ;) Budu argumentovat tím, že až bude náš kód někdo číst, bude se lámat hlavu, proč je tam název určen 2×, a bude jej to rozptylovat od práce. No a nakonec, neznám knihovnu, ani komponentu, která by tento styl používala.
Jak vidíš, je to trade-off. Pokud trváš na svém způsobu, dobře. Buď pak ale důsledný, a používej jej všude. A donuť i všechny ostatní programátory ve svém týmu, ať jej také používají ,–)
Teď je správný čas použít mou větu Kolikrát i záleží na kontextu, ve kterém autor JS uvažuje (použití v prohlížeči, na serveru, v databázi, …).
Mé použití JavaScriptu je ale opravdu velmi speciální (vyžadovalo i nějaké hooky v samotné implementaci)…:D
MS AJAX to v debug verzi používá, je díky tomu přehlednější call stack při debugování. Jindy to opravdu význam nemá.
ve vami uvedenem priklade ale pouzivate „function foo () {„, proto se to chova jinak. pri pouziti „var foo = function foo() {“ se chova samozrejme stejne jako „var foo = function() {„…
Nelibi se mi, ze nahore pisete, ze vsechno je objekt (i funkce) a ma spolecneho predka Object. Nize pisete, ze funkce je prosta definice a ze neni prirazena k zadnemu objektu. Tak jak tomu mam rozumnet?
„Neni prirazena k zadnemu objektu“ – tim autor mysli (alespon ja to tak chapu), ze funkce ve smyslu metody (tedy takova, ktera bude volana s teckou pred nazvem) neni svazana s tridou, u ktere je definovana.
Zatimco tedy v jinych jazycich piseme
class A { public method B() {} }
a je tak patrne, ze B „patri“ k A, tak v JavaScriptu zapis
A.prototype.B = function() {}
nevytvari zadnou logickou vazbu mezi (zcela normalni) funkci B a jeji (tridou) A. Uplne stejneho vysledku by bylo mozno docilit treba zapisem
var B = function() {}
A.prototype.B = B;
B je stale normalni funkce bez jakekoliv vazby na A.
Je to jednoduché. Snažím se vysvětlit, kdy se funkci říká funkce, kdy metoda, a kdy konstruktor. To pojmenování nám odlišuje, jakou roli funkce zrovna hraje. Když říkám „funkce je prostá definice a není přiřazena k žádnému objektu.“ tak to prostě znamená, že kdybychom funkci přiřadili třeba do prototype, už bychom jí správně měli říkat metoda. Mrkněte se na definici metody. „A method is a subroutine that is exclusively associated either with a class“. Už je to jasné?
Konecne sa objavil na zdrojaku po dlhom case clanok, ktory ma zaujal a prijemne prekvapil. S obsahom clanku suhlasim, aj ked si myslim, ze by to mohlo byt troska viac do hlbky, aby si pripadni programatori v js uvedomili, co sa ma pouzivat na co a ktore techniky su uplne nevhodne a preco. Js je totiz tak amorfny a ma tolko implementacii, ze zaciatocnici maju s jazykom velke problemy(tiez som ich mal a niektore dnes este mam:). Samozrejme by sa tym clanok predlzil a bol by asi nezazivnejsi a mohlo by to ludi odradit od citania.
Díky za komentář. Článek sem původně napsal v jednom kuse. Ale z důvodu délky mi byl rozdělen do třech dílů. Co se týká amorfnosti a množství implementací, souhlasím s vámi. Právě proto sem začal popisem špatných technik. Druhý díl se bude věnovat správné implementaci, snad vás mile překvapí.
Moc povedený článek, už se těším na pokračování.
Autora se chci zeptat jestli na konferenci internet developer forum bude obsah jeho přednášky stejný jako tento seriál. Díky
Ano bude. Na přednášce navíc půjdu více do detailů, a samozřejmě bude prostor pro otázky a diskuze.
Díky za článek, těším se na další díly.
Steigerwald: Přestože ten článek někdy je trochu zmatený (což nekritizuju, protože z principu javaskriptu snad každej článek o něm musí bejt zmatenej), tak až na detaily i přijde velmi dobrý.
Hrebenar: Promiňte, ale většina vašich výtek je mimo, zřejmě jste článek poněkud nepochopil. Ale stejně smyslem Vašeho komentáře bylo získat více prokliků na Vašem blogu a ten zcela určitě splnil.
(abych byl adresnej, tak např. u modulu závorky kolem a za anonymní funkcí jsou nutný, protože je potřeba tu funkci zavolat – jako modul neslouží ta samotná anonymní funkce, ale právě tím voláním vytvořené closure společně s
vrácenou funkcí
Nebo že objekt není asociativní (nebo asociované :-D) pole? Ono snad v JS nejde napsat funkce[‚a‘]=? Co se mění na pravdivosti tvrzení: „každý objekt je asociativní pole,“ se mění tím, že se dnes většinou objekty vytvářejí ze speciálního asociativního pole, které lze ještě navíc zavolat?
Nebo ad jQuery – uvedené řešení je jednoduchá berlička pro to, aby kód fungoval i když jiná knihovna přepíše $ definovaný v jQuery. Opět jsou v příkladu všechny závorky nutné.
Shrnul bych to takto: Zatímco autor tohoto článku byť někdy malinko neobratně se snaží vysvětlit jak javascript funguje uvnitř a teprve z toho vyvozovat závěry, vy jste jaksi spoután svým poněkud omezeným viděním javascriptu a
hlubší porozumění zaměňujete za chybu…
Pěkně jsem si početl. Těším se na další díl.
Pro ty, co to cetli 3× a stejne sotva pochopili – poslouchejte steidu (autor), jestli nekdo vi, o cem ve svem oboru mluvi, je to on. Tech par kritiku je typicka trolli zumpa, kterou se netreba zabyvat.
Nevěnuji se programování ecmascriptu zase tolik, ale v poslední době jsem se o možnosti OOP zajímal. Chtěl bych Vám nabídnout můj pohled na věc, co bych vlastně očekával od úvodního článku.
Psát pro širší publikum není snadné, ale stačí dodržovat některé základní postupy při psaní, které produkují lepší výsledky. Když se podívám na strukturu Vašeho článku, tak na první pohled vidím, že jste na ní příliš nepřemýšlel a psal jste, co vás napadlo. Je třeba začít osnovou a pak postupně formovat článek.
Především by na úvod neškodilo zmínit, co je vlastně míněno tím OOP v ecmascriptu se srovnáním, jak je to vnímáno jinde (rozhodně najdeme rozdíly v chápání OOP v ecmascriptu a smalltalku). Jeden odstavec by si to zasloužilo.
Rozhodně bych se vyvaroval nějakému přehledu pojmů, myslím si, že je vhodné vysvětlit pojem ve chvíli, kdy je potřeba a s krátkým příkladem. (Ty tam sice obvykle máte, ale docela by mě zajímala statistika kolik lidí na ně opravdu kliklo)
Celkově ten článek působí neuceleně, psaný horkou jehlou, což je zřejmé i z povahy diskuze, kdy jsou zpochybňovány Vaše znalosti. Také nesprávně.
Doufám, že si do příštích dílů z diskuze něco odnesete. Toto téma mě velice zajímá a rád bych se o OOP v JS dozvěděl, co nejvíce.
Nic ve zlem, ale nebylo by jednodussi priznat si, ze mi dela potize napoprve pochopit psany text? A v case usetrenem psanim zbytecneho komentare zkusit precist pecliveji jeste jednou? :)
Dan uz z povahy veci nepise pro siroke publikum, tema neni jednoduche, pokud vas to zajima – v klidu ctete a studujte, a na komentovani alespon na chvili zapomente.
Ano, i muj prispevek je zcela zbytecny.. :)
Je vtipné, že mi vnucujete studium, když vůbec netušíte, jaké znalosti mám nebo nemám. Nevím čím jsem podle vás dal najevo, že bych článek nepochopil. Můj komentář nabídl pohled na formu článku pro autora. Dobře napsaný článek na téma OOP v Javascriptu si představuji jinak.
Pokud Dan nepíše pro široké publikum, tak by neměla téměř celá první polovina článku obsahovat naprosté základy jako co je to objekt, kontext nebo scope. Pokud to čtenář neví, tak nemá smysl, aby ten článek vůbec četl. Je třeba přemýšlet nad tím koho chci oslovit.
Souhlasím s vámi, váš příspěvek je zbytečný ;)
Jenže začátek článku není vysvětlení základů, ale definice použité terminologie. Je vidět, že jste to, stejně jako většina diskutujících tady, ještě nepochopil. Což je smutné.
> když vůbec netušíte, jaké znalosti mám nebo nemám.
No, možná, že jste s tím po zdejším obyčeji začal sám a docela zhurta:
>> Když se podívám na strukturu Vašeho článku, tak na první pohled vidím,
>> že jste na ní příliš nepřemýšlel a psal jste, co vás napadlo.
WTF? Tady na Zdrojáku se s železnou pravidelností vyskytují jasnovidci. Autor „psal, co ho napadlo“…
>> Především by na úvod neškodilo zmínit, co je vlastně míněno tím OOP (…)
Ale. To je docela laciná „kritika“, ne? (Ta debata s pány gisat, peter, atd o něco výše, to není kritika, ale komika. Taková řekněme „krampolovská“.) To bychom si taky mohli začít vykládat že JavaScript se původně jmenoval _Mocha_ atd atd atd.
>> (…) vyvaroval nějakému přehledu pojmů (…)
Z kontextu článku je ale zjevné, že _právě_ přehled pojmů na začátek je potřeba, včetně vysvětlení, proč je dle autora lepší anglické pojmy _nepřekládat_ do češtiny. Neboť většina žabomyších hádek okolo JS u nás se zamotá už v tom, že _scope_ nazývá každý nějak jinak a především tomu vlastně na 100% nerozumí, ale rozumí tomu jen trošičku a o tu trošičku se taky bude hádat.
Článek je vynikající. Těším se na další díly. Je nesmírně osvěžující přečíst si něco a přímo fyzicky v tom _vidět_ to porozumění věci. To se stává zřídka.
(Už jsem třikrát smazal nějakou reakci na místní jasnovidce a kritiky, tak tentokrát již odešlu. Aby to nevypadalo, že to tady čtou jenom mašíblové.)
Martine, článek se původně jmenoval „Třídy, dědičnost a OOP v Javascriptu“, ale titulek mi byl zkrácen, a článek samotný rozdělen do tří částí. To se nemohlo nijak neprojevit ;)
Srovnání rozdílu s klasickými třídami jsem slíbil v úvodu, a článek jej obsahuje, v druhém a třetím dílu ;)
Nebojte se, dovíte se vše podstatné.. seriál je už napsán. První díl berte jako takový úvod. Nechtěl sem čtenáře zahltit pojmy jako agregace, mixování, dědičnost, volání přepsané metody, atd.
To je celkem škoda, protože ten titulek vystihuje téma mnohem lépe a taky by nedošlo k některým nedorozuměním. Až bude celý seriál venku, tak určitě bude působit kompaktněji. Díky
Hele, ještě že čteš ty náhledy, co? :)
Jsem ohromen. Tohle nepamatuju, nemam horkost? Teplota v analu… 36.6? V norme. Rooote! Nezblaznil ses? Zrak ok, alkohol v krvi v norme… Kurwa, ja ten clanek precet az do konce. Chapete to? Ja to precet az _DO_KONCE_ Hrabu se, hrabu, hrabu, hrabu… Kde se asi muzu hrabat? V pameti se hramu. A nejak se nemuzu dohrabat. Kdy se vam naposled stalo, ze byste tu neco docetli az do konce?
Dane, chvalit te nebudu, protoze bys spychnul a pokracovani uz bych pak treba nedocet. Kazdopadne, mas me simpatie. :-)
A já přečetl tenhle názor až do konce, protože jsem byl zvědavý, jaké bude rozuzlení :D
To myslite ironicky, alebo vazne? :)
Dufam ze ironicky. Ak vazne, volajte si 112.
chapu ze z me reakce nemuselo byt uplne jasne, jestli se mi clanek libil nebo ne. Takze, to shrnu, libil :-)
A volali ste? :)
rval sem to jak na lesy, stoooooodvanaaaaaaaaaact… a nic :-)
To je jasna diagnoza. :)
Javascript aktivně nepoužívám, rád ho nemám, ale přesto jsem článek přečetl jedním dechem :).
Takže Dane, jako prostý laik ti děkuju za článek, odborníci na JS ti v komentářích určitě ještě každou větu rozcupují, aby se jim lépe spalo.
Blízkost hnoje přitahuje masařky, blízkost Rootu evidentně hovada.
Dříve platilo, že co Čech, to muzikant. Dnes, co Čech, to evidentně expert na javascript. A všichni JS experti si dnes dali sraz na Zdrojáku.:)
Pokud se vykašlu na privátnost, pak může nastat problém u potomků, které přeci nemusí zajímat, jaké mám privátní vlastnosti a metody, ne?
Samozřejmě, v případě preference kompozice před dědičností (správně) to je většinou OK, ale chtěl jsem ukázat, že privátní viditelnost má smysl nejen co do dokumentace.
OT: Dál bych byl v seriálu rád za nějakou budoucí zmínku o volání parent konstruktoru.
Položil jste velmi dobrou otázku, a já jsem za ni rád. Přesně takovéhle dotazy obohacují článek. Nejprve ale odpovím na OT: Ano, ve druhém i třetím díle si ukážeme správné (i špatné) volání nejen parent konstruktoru, ale i libovolné jiné, třeba přepsané, metody.
Teď k těm privátním. Nerad bych předbíhal, ale potomek nikdy nezavolá privilegovanou metodu kterou, jak jsme si už řekli, používá Crockford k přístupu k privátním proměnným. Nezavolá, protože metoda začne existovat, až v okamžiku vytváření instance, čili, není součástí prototype. Museli bychom vždy povinně volat rodičovský konstruktor, nebo to nějak hacknout (že to lze prasácky nějak obejít, to nechci ani naznačovat ;)
Můžete namítnout: „A co když metoda sama bude privátní?“ To je případ, který záměrně ve výkladu nezmiňuji. Považuji jej totiž za zbytečný, náchylný k chybám, a svádějící k falešnému pocitu nějakého cool zabezpečení kódu. Je to relikt z ostatních jazyků, který dle mého jde proti duchu Javascriptu, a jehož použití je oprávněné pouze v příkladu s jQuery, microoptimalizací, a podobných vzácných případech. Přesto, jednu implementaci vám ukáži. Podívejte se na tento příklad, ale neberte si z něj příklad ;) http://jsfiddle.net/8EtRB/
Máme kvazi-privátní (ve skutečnosti lokální) metodu toString. Jestli je instanční, nebo statická, závisí na tom, jak metodu voláme. Takto definovaná metoda, může i využívat nějaké privátní statické proměnné, případně manipulovat přímo s instancí, pokud ji instanci předáme. Co je na tom příkladu tedy zlého? Za prvé, jedná se o zásadně jiný zápis. Zatímco v jazyce, který privátní členy podporuje klíčovým slovem private stačí použít klíčové slovo, zde musíme využít closure. Kód se bude hůře refaktorovat. Za druhé, tento zápis nejde změnit, což by nám později vadilo (v posledním díle si ukazujeme jak deklarovat s třídy nejen správně, ale i úsporně). Za třetí, a to je hlavní důvod, je to zcela zbytečné. Proč bych měl v jazyce jako je Javascript něco zapouzdřovat?
Já reagoval na „Mírně to naznačíme podtržítkem v názvu, brutálně pomocí closure. Jestli někde má smysl simulovat privátní proměnné pomocí closure, tak jedině u statických objektů, tedy modulů.“.
BTW: Poslední způsob vytváření třídy taky není zrovna chválen, ale věta „Ukážeme si dvě falešné a jednu správnou.“ napovídá, že by to mohl být ten správný. Asi by to chtělo trošku korekci srozumitelnosti. Já vím, asi to bylo myšleno tak, že to není správný způsob, ale vznikají při něm skutečné třídy.
Sakra to jsou zmatky, měl sem seriál od začátku psát na limit délky jednoho článku, a ne čekat, že to půjde nějak dobře rozdělit ;) Ne, ani poslední uvedený způsob není správný. Správnému způsobu bude věnován celý druhý díl :)
To je celkem jasný. Já ze článku pochopil (nečetl jsem úplně důkladně, něco znám), že správný způsob budou asi ta podtržítka. A na to byla ta moje reakce.
Jinak za informace o parent constructor call děkuju předem.
Pokud se nemůžete dočkat, můžete mrknout na můj článek, kde volání konstruktoru parenta používám ;-)
A už sis jej opravil? ,–)) Koleduješ si o brutální review :)
Jinak, abys mne špatně nepochopil, článek je dobře a poctivě napsán. Jen obsahuje několik strašlivých chyb :o)
Já vím ;-) Některé jsem si uvědomil s odstupem času, ještě dřív, než jsi začal rýt ;-)
Review, resp. přepis bez chyb, jsem právě plánoval tady na Zdrojáku, ale teď mám obavu, že mi to nepovolí, aby tu nebylo přejavascriptováno… Tak asi radši napíšu něco o ASP.NET MVC :o) Ať Ti nelezu do zelí :)
No, prave pasaz o dedicnosti je v tvem jinak dobrem clanku slabinou. Tvrzeni, ze dedicnost v JavaScriptu neexistuje je jadrem problemu. Za takoveho predpokladu by byl i tvuj zpusob volani konstruktoru predka dobrym zpusobem, jak si dedicnost doplnit.
Jenze Javascript dedicnost primo podporuje. Tu prototypickou. O ni a o problemech, ktere nam prinasi – prototyp vs. inicializace instance – bude pocitam pristi dil, takze bych uz v tomto tematu dale nepredbihal :)
A nebo můžete používat knihovnu JAK a používat dědění a volání metod předka jak je vám libo :) http://jak.seznam.cz
Souhlasím s Martinem Staňkem, že by si článek zasloužil lepší strukturu. V žádném případě nechci zpochybňovat znalosti autora o JavaScriptu (je vidět, že je má), ale stylisticky působí článek trochu zmatečně. Vedle toho bych chtěl poukázat na větu:
„Douglas Crockford udělal pro svět Javascriptu hodně, ale některé články, věnované OOP a dědičnosti, se mu zrovna nepovedly.“
Nevím kde autor bere tolik sebevědomí, že kritizuje Douglase Crockforda, senior JavaScript architekta firmy Yahoo! a člena komise, která určuje vývoj jazyka JavaScript, za jeho články. Nechci být hrubý, ale možná by si měl autor sednout na prdel a zamyslet se nad vlastními články.
Autor na prdeli sedí více než je zdrávo, a věřte že seriál by nepsal, kdyby si svá tvrzení nemohl obhájit.
Uvidíte, že na konci seriálu se mnou budete souhlasit ,–) Vždyť i Crockford sám přiznává, že jeho začátečnické pokusy byly pomýlené. („… now I see my early attempts to support the classical model in JavaScript as a mistake.“) A to se týká nejen odkazovaného článku, ale třeba i tohoto, nebo tohoto. Jeho články jsou plné falešných a zbytečných doporučení, jeho techniky dědičnosti a OOP, jsou ošklivé jak hřích. (krom jedné jediné)
Pochybujete? Tak jak vysvětlíte, že YUI (oficiální Yahoo framework), ve své poslední verzi žádné jeho techniky nevyužívá? Dokonce ani oblíbenou manýru zvanou: Modul S Konečně Supr-Dupr® Privátními Proměnnými™.
Děkuji za odpověď. Články, které uvádíte, jsem četl, a i přesto, že Crockfordovi původní techniky se v moderních frameworcích už nepoužívají, stále mají články s jejich popisem velkou informační hodnotu a v žádném případě bych je nepovažoval za „nepovedené“. Naopak v nich lze vyčíst postupný rozvoj programovacích technik používaných v JavaScriptu.
V každém případě rád vidím články o JavaScriptu na Rootu a těším se na pokračování seriálu.
Pamatuju ještě černý Root, ale na takhle špatný si nemohu vzpomenout
1/ JS samozřejmě třídy nemá, citovat wiki a ještě takovou hloupost je úsměvné, to je definice pro někoho, kdo nikdy o OOP neslyšel. Programátor musí vědět, co to znamená „třídní programování“: zapouzdření, dědičnost a polymorfismus, přičemž JS poslední 2 vlastnosti neobsahuje. To, že se dají obejít (rozšiřováním předpisů, přiřazování prototypů) ještě neznamená, že má třídy. JS má typy, má objekty a nikdy nemělo třidy. Když už píšete články pro programátory, citujte alespoň z trochu relevantních zdrojů, jako například specifikace ECMA (http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf), strana 3: ECMAScript does not use classes.
To, čím se JS tolik liší od ostatních jazyků je právě neexistence tříd, ale existence prototypů. Neexistují klasické třídy a neklasické třidy.
Pojem tříd a instancí nemá v článku žádný význam, pokud to někdo chce číst v souladu s realitou a správně, čtěte typy a objekty.
Věty jako „Falešné, protože ve skutečnosti nejde o vytváření instancí tříd, ale o konstrukci podobných objektů.“ jsou potom už jenom vtipem.
2/ JS nemá nic takového, jako primitivní typy, to, že mžete napsat
var num = 5;
je jenom deklarací objektu typu Number jeho literálem, pokud by se jednalo o primitivní typ, následující konstrukce by nebyla možná:
var a = {'num': 5};
alert(a.num.toString());
věřte, že funguje skvěle
3/ Následně se článek bohužel už věnuje „třídám“ a „instancím“ a z tohoto hlediska ho nemá smysl číst.
Bez ohledu na to, jak dobrý je pan Steigerwald programátor, tak jeho způsob prezentace a vysvětlování JS je strašný a dělá JS medvědí službu. Každý, kdo se kdy setkal s třídami v OOP se musí při seznamování s JS dozvědět hned na začátku jednu věc: JS třídy nemá, zapomeň na OOP tak, jak ho znáš z jiných jazyků. JS má prototypy a ty nemají s třídami nic společného.
Špatný komentář, ale zdaleka ne nejhloupější zde :-)
ad 1) pokud je definicí třídního programování zapouzdřenost, dědičnost a polymorfismus, znamená to, že např. Python, který nemá zapouzdření členů tříd, není „třídním“ jazykem? To by bylo hodně odvážné tvrzení, co?
ad 2) budete velmi překvapen, ale typeof num nevypíše Number :-) (na velikosti záleží). Dokonce si můžete vyzkoušet, že čísla jakožto primitivní typy se do funkcí nepředávají referencí, narozdíl od objektů. Atd.
Špatný komentář, ale zdaleka ne nejhloupější zde :-)
1) Medzi jazykmi Python a Javascript je dooost velky rozdiel co sa tyka tried. Python triedy ma a Javascript nie.
dle meho intuitivniho citeni, ktere ma asi tak stejnou vahu jako vase neduvera v odvahu vami nastineneho potencialniho tvrzeni, je jazyk bez zapouzdreni (ale s dedicnosti a plymorfismem at uz to znamena cokoli) vice tridni nez jazyk bez dedicnosti..
jinak si tez myslim, ze tridni jazyk je ten, jehoz OOP je zalozeny na tridach, coz JS neni
a kamenujte me
Neviem ake su Vase znalosti jazykov Python a Javascript, ale skutocne Javascript triedy na rozdiel od Pythonu nema. Kde ste videli triedy v Javascripte?
Pane Bronislave Klučko (jste to vy: http://www.bauglir.com, že?), spravedlivý hněv vás zaslepil, ale to nevadí, to se občas stane každému.
Váš komentář vítám, protože mi dává příležitost opět vyvrátit pár omylů. Pojďme si je tedy rozebrat. Opakování je matka moudrosti ,–)
1) Je častou chybou programátorů začátečníků, brát všechny pojmy dogmaticky. Možná vás to překvapí, ale co platí v jednom programovacím jazyce, nemusí platit v jazyce jiném. Jedním z takových pojmů je třída. Každý jazyk ji definuje po svém, a Javascript není výjimkou. V úvodu, který jste určitě bleskem pouze přelétl, už už aby jste mohl komentovat, jsem zdůrazňoval, že jde o konsensus mezi autory Javascriptových knihoven. Koukněte: dojo, mootools, yui, google closure, prototype, qooxdoo. Všichni hovoří o třídách. Za třídu se v Javascriptu označuje konstrukční funkce, používající vlastnost prototype. Už vidíte, jak moc se mýlíte?
Mimochodem, když už chcete citovat ze specifikace, poprosil bych o celou větu až dokonce. Ta zní: „ECMAScript does not use classes such as those in C++, Smalltalk, or Java.“ Hned to zní jinak, že? :-) Dokonce i specifikace mluví o třídách, pouze zdůrazňuje, že jsou jiné.
2) Javascript samozřejmě primitivní typy má. Když už tu mlátíte specifikací o stůl, tak si snad zvládnete i dohledat větu: „A primitive value is a member of one of the following built-in types: Undefined, Null, Boolean, Number, and String;“. Vy ale zcela evidentně nevíte, co primitivní znamená. Vážně jste si nikdy nevšiml, že se v Javascriptu některé typy předávají referencí, a jiné hodnotou?
Byl bych k vám krutý, kdybych nevysvětlil, jak váš příklad ve skutečnosti funguje.
Tento zápis skutečně deklaruje primitivní hodnotu. Zkuste předat
num
do nějaké funkce, tam jej inkrementovat. Změní se původní num? Nezmění, protože je primitivní a byl tedy předán hodnotou. Dále si můžete vyzkoušet přiřadit nějakou hodnotu:Sakra, nejde to. Proč? Protože num je primitivní :-D
Jiná situace by byla, pokud byste napsal:
S takto vytvořeným číslem můžete pracovat stejně jako s jeho literárním zápisem, avšak pozor, num se předává referencí. Schválně si to zkuste, protože je to pro vás evidentně nová věc. Pomalu se blížíme v rozuzlení vašeho (krapet ukecaného) příkladu. Proč ukecaného? Protože stejného výsledku dosáhneme i takto:
Jak je vidět, ani nemusíte vytvářet objekt ‚a‘ :-)
Shrnutí. Literární zápis generuje primitivní hodnoty, zatímco zápis pomocí konstruktoru (Number, Boolean, String) objektové reprezentace. Převést primitivní hodnotu na její objektovou reprezentaci, lze pomocí přímého volání konstruktoru, ani nemusíme použít new.
Obráceně to za nás Javascript udělá vždy, když detekuje, že by se primitivní hodnota hodila, nebo to lze provést explicitně, a jak ukazuje poslední alert, dokonce můžeme používat vlastní objekt, číslo imitující.
Za domácí úkol si spočítejte, kolikrát je pojem ‚primitive‘ zde uveden.
3) K poslednímu bodu bych dodal, že právě Vy, byste si měl článek poctivě pročíst, a to ve svém vlastním zájmu.
> Je častou chybou programátorů začátečníků, brát všechny pojmy dogmaticky.
Problém je v tom, že když nepoužíváte přesné a jasné definice základních pojmů, tak se pak vlastně neví, o čem mluvíte. Odstrašujícím příkladem může být pojem OOP. Když někomu řeknete, že jazyk XYZ podporuje OOP, tak je to jako byste nic neřekl (možná říkáte, že jazyk podporuje dynamic dispatch, ale to je asi tak všechno).
Aj ja som za striktne definovanie a dodrziavanie pojmov. Je to znak profesionality.
Kto sa nevie vyjadrovat, nech nepise clanky, lebo narobi viac skody ako osohu.
A já ne. Dogmatické a striktní chápání jakéhokoliv pojmu vidím jako programátorovu slabinu a jeho neschopnost. Místo chápání konceptů se brání definicemi pojmů.
Dobrý programátor je schopný používat koncepty a dokonce o nich i komunikovat s ostatními bez takovéhleho dogmatismu.
Dan Steigerwald je jeden z nejlepších JS programátorů v Čechách a článek který napsal čouhá nad českým průměrem znatelně. Možná snad jen to je jeho chybou – tedy že je článek určený inteligentním, uvažujícím lidem.
> Dogmatické a striktní chápání jakéhokoliv pojmu vidím jako programátorovu slabinu a jeho neschopnost. Místo chápání konceptů se brání definicemi pojmů.
Ako mozete chapat koncept, ked nechapete pojmy?
> Dobrý programátor je schopný používat koncepty a dokonce o nich i komunikovat s ostatními bez takovéhleho dogmatismu.
Mam pocit ze Vy asi inak chapete aj moju vetu „striktne definovanie a dodrziavanie pojmov“. To potom ale vedie na styl diskusie „Ja o voze, ty o koze“, co je presne teraz nas pripad.
Dogmatické chápání pojmů je základem, protože (teď nemluvím o panu Steigerwaldovi, dávám obecné příklady) když si někdo bude plést proměnnou a typ, funkci a metodu, hodnotu proměnné a pointer na proměnnou, a já nevím, co ještě, tak to není dobře. Uvědomte si, že toto všechno nejsou jen tak pojmy něčeho abstraktního, ale naprosto konkrétní koncepty, které pokud zaměníte v kódu, nastane průšvih. Navíc se tu nebavíme o nějakém klábosení ajťáků večer u piva, ale o naučném článku, který by měl být maximálně přesný, protože jinak mate.
Nejde o konceptuální dogmatizmus, fakt, jestli tomu říkáme, nebo neříkáme třída nemá nic společného s tím, jestli jsou třídy oblíbené, používané, dobře používané… Nikdo se tu nehádá o tom, jestli je prototypování lepší, než třídy, nebo ne.
Prohlášení o tom, že někdo, kdo je schopný programátor + má publikační činnost patří k jedněm z nejlepších programátorů mě vždy rozesměje. Teď se nechci pana Steigerwalda dotknout osobně, tato výtka se ho netýká, ale pane Nešetřile, znáte VŠECHNY programátory v Čechách, abyste mohl takhle hodnotit? Navíc od kdy se dobrý programátor = dobrý publicista?
Neinteligentní Neuvažující Člověk
> Místo chápání konceptů se brání definicemi pojmů.
Tady nikdo neříká, že to má být na úkor chápání konceptů.
Koncepty se hodí, když o problému přemýšlíte v „obecné rovině“. Ale až vymyslíte, jak se problém dá vyřešit, je potřeba ověřit, že váš postup je správný. A zde už si s koncepty nevystačíte. Zde už potřebujete zadefinovat problém, popsat algoritmus na jeho řešení a dokázat, že řeší definovaný problém.
Jak souvisí algoritmizace kokrétně s JavaScriptem? V JS jde hlavně o reaktivní proces…
V JS jsou platné především koncepty.
To měl být jen příklad na to, že s koncepty nevystačíte.
tohle je docela zajimave, co jste nevedomky zminil – algoritmus – tenhle clanek byl o vsem jinem nez o algoritmech, a prijde mi to symptomaticke, zel i pro me vlastni zamestnani ..
Děkuji za reklamu, jsem to já :)
ad 1/ ano, specifikace mluví o třídách asi na 5 místech… všude, kde vysvětluje, v čem se JS liší od třídních jazyků.
ad 2/ konsenzus je dohodou/porozuměním mezi lidmi, na faktech ale obvykle nic nemění :). Můžete si tomu říkat třeba druh ovoce ale ovocem se to nestane. JS prostě nemá třídy :)
Fakt, že tomu Vy a další budete říkat třídy vede jenom k tomu, že se programátoři, kteří k JS přijdou z jazyků, které podporují třídy, budou snažit opakovat stejné postupy, na které jsou zvyklí a oni prostě v JS nepůjdou… Když to řeknu jinak, pokud budu přecházet mezi ObjectPascalem, PHPkem a C#, změním spoustu věcí, syntaxi, knihovny, frameworky… ale nezměním paradigma pro vytváření objektů. Pokud se ale dostanu k JS, tak třídní paradigma musím zahodit a musím začít přemýšlet v prototypech.
ad 3/ nemám problém přiznat, že s primitivními typy jsem se sekl. Nicméně i Vy máte Vašem rozsáhlém popisu chybu:
„Převést primitivní hodnotu na její objektovou reprezentaci, lze pomocí přímého volání konstruktoru, ani nemusíme použít new.“
Samozřejmě musíte, zkuste si pro změnu i Vy nějaké příklady
var a = Number(2);
var b = new Number(5);
a.data = 5;
b.data = 5;
alert(a.data + "n" + b.data + "n" + (typeof a) + "n" + (typeof b));
i specifikace jasně píše: „The Number Constructor Called as a Function: When Number is called as a function rather than as a constructor, it performs a type conversion. Number ( [ value ] ):
Returns a Number value (not a Number object) computed by ToNumber(value) if value was supplied, else returns +0.“
4/ nebojte se, já si to pročetl a pročtu si i další díly, některé věci, které jsem ani nekomentoval, protože předpokládám, že budu mít možnost v jiných částech
Díky za upozornění, mělo tam být Object místo Number. Ve čtyři ráno už člověk není tak pozorný ;) „When Object is called as a function rather than as a constructor, it performs a type conversion.“
Naprosto souhlasím (jsem také čtenářem ECMA-262). Uvidíme ale až v pokračování seriálu, jestli detailní vnímání specifikace a přesné chápání pojmů bude mít vliv na pochopení souvislostí, protože i nesrovnalosti v pojmech se často řeší dobře zvolenými příklady. A nakonec se stejně lidé vracejí k příkladům, než k textu. Učebnice z tohohle seriálu prostě nebude, informační hodnotu ale mít bude.
Nechci vypadat jako hnidopich, ale z tehle obsahle diskuse mam cim dal vic pocit, ze javascript je velmi nepovedeny jazyk. Proc se musi objevovat techniky jak udelat tohle a jak tamto, misto toho, aby to bylo zakotveno primo ve specifikaci jazyka? Prijde mi to stejne spatne jako zakony – potrebujete k nim pravni vyklad, abyste vubec vedeli, co se tim zakonem vlastne chtelo rict.
Nejsem zadny odbornik, ale treba actionscript mi prijde jako mnohem povedenejsi scriptovaci jazyk (klicova slova jako package, class, extends napovi…)
Možná bych nahradil přívlastek „nepovedený“ za „komplexní“ či „složitý“. S dodatkem, že na první pohled vypadá jednoduše, ale zdání klame.
Zakotvení některých principů ve specifikaci by situaci jistě svým způsobem usměrenilo, jenže právě nesmírná flexibilita a expresivnost jazyka je jeho hlavním atributem.
Lehce negativním důsledkem pak je, že jedna „technika“ (v tomto případě OOP) je v praxi realizovatelná několika různými způsoby. Jejich přednosti, ale hlavně nevýhody, pak (doufám) jsou/budou zmíněny v této i dalších kapitolách článku.
Rozumný čtenář si stejně nakonec vybere sám – za předpokladu, že korektně pochopí všechny principy, bude jeho volba jistě správná.
Komplexnost a zlozitost jazyka je zial v pripade Javascriptu a jeho nasadenia dost nevhodna.
Techniky riesenia mennych priestorov a dedenia su problematicke.
Myslim, ze napriklad Python by bol ako potencialna nahrada Javascriptu omnoho produktivnejsia a vhodnejsia pre beznych programatorov.
tohle se urcite nebude libit – protoze mozna urceni, duvod existence, ucel JS byl puvodne byt skriptovacim embedded jazykem a to embedded zejmena uvnitr nejakych jiz existujicich datovych struktur (prohlizec – DOM), proto ten duraz na lehkou modifikaci prostredi (prototypovy objektovy system, moznost prilepit si cokoli kamkoli) a mensi na vytvareni rozsahlych veci od nuly (vypusteni modulu, ne tridy)
a k tomu ucelu ho ohybat potreba asi nijak zvlast neni/nebyla
no ale lidi delaj vsechno a kdyz tam jsou plne closures, pak lze i toto
jelikoz se doba posunula a ten ucel se asi trochu posunul, tak ted dle Wikipedie ( http://en.wikipedia.org/wiki/ECMAScript#Features_2 ) jsou asi i ty tridy na poradu dne..
1.) javascript (soucasna verzi) je hodne jednoduchy a minimalisticky jazyk, moc konceptu tam neni, proc do nej zavadet pojem „trida“, kdyz OOP system, ktery pouziva, je tzv. prototypovy a „klasicka“ OOP (dejme tomu C++ cesta) terminologie zde nedava smysl?
2.) scope je velmi jednoduchy koncept, nevynalezl ho javascript a neni na nem nic sloziteho, priklad s mercedesem v garazi se ztmavenymi okny je naproste psycho, asi jako zmrvit vtip a divat se na taktni utrpne usmevy lidi kolem
3.) closure neni jiny nazev pro scope, tak jak to chapu, je to vec implementace scope, scope rozhodne maji i jazyky, ktere closury s tou funkcionalitou, na kterou jsme zvykli z funkcionalnich jazyku, neimplementuji
4.) „V Javascriptu platí, že vše co není primitivní typ je asociativní pole, krátce objekt. Funkce je objekt..“ → vyplyva z toho, ze funkce je asociativni pole? a je?
zbytek jsem necetl, kecy nectu
ad 1) pojem třída už zaveden je a cílem článku je ho vysvětlit. Zkuste spíš zvážit, jestli je C++ skutečně ono „klasické OOP“, nebo spíš jestli neexistuje OOP v podání C++ stejně jako OOP v podání JavaScriptu. C++ je poměrně mladý jazyk, batole proti OOP.
ad 2) scope JavaScript nevynalezl. Kdo to tvrdí? Proč to vyvracíte?
ad 4)
Ehle, je to tak, funkce je asociativní pole. Mýlil jste se, takže článek by pro vás byl velmi užitečný.
1.) C++ (popr. jazyky, ze kterych vychazi jeho OOP – a jejich terminologie) je starsi nez JS, „popularizoval“ li se v souvislosti s tim C++ nejaky pojem (trida napr.), je trochu hloupe, kdyz ho nekdo pozdeji ohyba, muj nazor
2.) narazel jsem na velmi neobratne vysvetleni, na tu nepruhlednou omacku + nektere komentare, ktere z toho delaji neco, co to neni, kdybych napsal „vysvetlovanim toho, co to je scope, objevujete kolo (v pripade, ze to delate neobratne za pomoci mercedesu) a nosite drivi do lesa“ bylo by to srozumitelnejsi“? Konkretne k te posledni otazce (vyvraceni) – protoze chci, prekvapive, vim, zvykejte si.
4.) nevedel jsem to, pripoustim, ze jsem spis predpokladal opak, ale proto jsem se ptal, abych se to dozvedel; ne ze by to menilo neco na tom, jakym zpusobem JS (nebo funkce v JS) pouzivam (nebo spis nepouzivam, ackoli se mi libi – kvuli neexistujici nativni implementace s nejakou relevantni mnozinou knihoven – neco jako Jscript.NET, pro ktery jsem ovsem nenalezl nejake podpurne nastroje)
muj hlavni dojem – mel-li to byt clanek o tom, kterak implementovat tridni objektovy system pomoci closures, pak to stacilo napsat, neni v tom vubec nic JS specifickeho, cili se dala vypustit cela ta omacka, mateni (pod pojmem OOP v JS si clovek asi predstavi neco jineho) a neumele vysvetlovani pojmu..
ad 4) Neboj se. Tvrzení, že funkce či objekt je asociativní pole, vystihuje sice na první pohled podstatu, není to ale vůbec přesné. Asociativní pole je totiž pouze datová reprezentace, zatímco objekt a funkce jsou složitější pojmy. Zůstanu proto jen u objektu, což je v JS množina vlastností (hodnot, prototypu, funkcí, konstruktorů…) – slovy OOP by se dalo říct, že obsahuje atributy a metody.
Říkat, že objekt je asociativní pole, je hodně zjednodušující (i když pro mnohé začátečníky dostačující).
Např. pro datový typ Array – navzdory tomu, že to je Object – to třeba neplatí, protože operace přiřazení (x[4]=5) nepřiřazuje pouze ke klíči 4 hodnotu 5, ale zároveň upravuje vlastnost (atribut) length.
Jiný příklad nalezneme hned u další vlastnosti JS – prototypové dědičnosti. Zde také naráží myšlenka asociativního pole, protože se při nenalezení klíče v dané instanci hledá v objektu schovaném pod klíčem prototype (pak případně i v jeho prototype a tak dále, dokud už další prototype neexistuje).
Na objekty tedy lze s jistou dávkou zjednodušení pohlížet jako na asociativní pole, je to ale velmi chytré (až vyčůrané) asociativní pole :-)
Tenhle článek obsahuje spoustu takových nepřesností. Nu což, někomu to prostě vadí, někomu ne. Já si myslím, že až bude článek kompletní, tak si v něm každý něco najde. Ten, komu vadí pletení pojmů, se může od pojmů odtrhnout a odnést si jádro věci. No a ostatní se na pojmy zas tak nekoukají, takže si odnesou to samé :-)
> ad 1) pojem třída už zaveden je a cílem článku je ho vysvětlit.
Prosim mohli by ste nam predviest ako zapiseme triedu v Javaskripte? Myslim skutocnu triedu/typ a nie prototyp objektu.
to samozrejme nejde; tridni OOP lze zavest pomoci closures, jak je ukazano v clanku, coz je o dost pohodlnejsi, nez simulovat OOP v C, coz ale rovnez lze a rovnez to z C nedela objektovy jazyk alespon dle dnesnich obvyklych standardu vnimani tohoto terminu
Kedze neexistuje trieda, nie je ziadne „triedne“. Javascript ma prototypy. Je to prototypovaci objektovy jazyk a nie triedny. Ci si Vy osobne, alebo autor clanku zavediete nejaky pojem a budete ho pouzivat na veci nic nemeni.
Docela by mne zajímalo, jaká je Vaše definice třídy. Podělíte se s námi?
Ziadna definuicia triedy pre Javascript neexistuje!
Specifikacia Javascriptu ten pojem nepozna a je zbytocne sa o triedach v kontexte Javascriptu ako jazyka bavit.
Chapete? Asi nie. :(
vid. bod 1 v http://zdrojak.root.cz/clanky/oop-v-javascriptu-i/nazory/8009/
Nesouhlasím. Specifikace JavaScriptu sice pojem třída nezná, ale proč bychom si ho nemohli zavést? Pojem „třída“ pro JavaScript bych přirovnal v návrhovému vzoru.
Jestli má tento „návrhový vzor“ v případě JavaScriptu smysl a je dobré ho používat, to je věc na další diskuzi…
Skuste napisat clanok o 3D grafike a namiesto „vertex“ pouzivat slovo „point“, namiesto „edge“ pouzivat slovo „line“ a namiesto „face“ pouzivat slovo „plane“. Asi vas za odbornika povazovat nebudu hoci ste uz co to v 3D naprogramoval.
Pojem „trieda“ je vseobecne znamy, definovany a zauzivany v jazykoch ktore triedy maju. Pretazovat pojem „trieda“ len preto, ze autor clanku nema v slovniku dost slov je nevhodne a nehodne skutocneho odbornika.
Alebo sa chcete vy, alebo autor clanku, preslavit ako vynalezca tired v jazyku, ktory triedy nema?
Ako vidite vasa originalna trminologia vyvolava iba nepokoj a vlnu nedorozumeni v diskusii.
Hovori sa: „Ak pomenujete v jazyku C parametre funkcie main inak ako argc a argv je to urazka vsetkych skutocne dobrych programatorov v jazyku C.“
Hovorit o triedach v Javascripte je nieco podobne. :(
Tak už sem konečně hodně tu vaši správnou definici pojmu třída, ze které bude zřejmé, že Javascript třídy nemá. Nedáte-li ji sem, pak jste jenom otravný troll.
Dane, nejsem žádný OOP expert. mé objektové vzdělání je omezeno pouze na C++, PHP, Python a Javu. pokud ale z těchto čtyř vyvodím nějakou definici třídy (a nechtěj prosím abych ji psal), dojdu k závěru, že JS třídy nemá (někde uvnitř hlavy mám dvě množiny „třídní OOP“ a „prototypové OOP“. nemají průnik)
pozor, to nám ale nijak nebrání třídy v JS používat! :P
když jsem kdysi poprvé zkoumal možnosti třídního OOP v JS narazij jsem na myšlenku, že třídní vlastnosti mohou být pomocí jiných prvků jazyka ‚emulovány‘. s takovou definicí jsem osobně naprosto spokojený
velice ti děkuji za článek a těším se na příští
Mozna je na case naucit se nejaky JINY jazyk :)
Vase argumentace je podobna tvrzeni: „Jezdil jsem uz ve Felicii, ve Fabii i v Oktavce, a vsechny mely masku chladice v podobe svislych mrizek. Proto musi byt maska chladice vzdy ze svislych mrizek“.
Ale tady to asi někdo vůbec nepochopil! Šup šup, přečíst můj „názor“ ještě jednou ;-)
Já neříkám, že by se mělo místo zavedených pojmů používat něco jiného. Já říkám, že může být vhodné zavést označení pro nějakou typickou konstrukci, de facto tedy pojmenovat návrhový vzor.
V některých jazycích prostě třídy jsou v základní syntaxi (např. C#), v některých ne (např. JavaScript) a tudíž může být vhodné si to tam nějak dobastlit.
Je to podobné, jako by tu byl třeba článek „Implementace singletonu v C#“ a Vy jste tu vykřikoval, že ve specifikaci C# žádný singleton není a tudíž je úlná blbost o něčem takovém psát, že to přináší jen zmatení a že přece hlavní metoda programu by se měla jmenovat Main.
Btw. jestli jste mě chtěl oslnit svými znalostmi z počítačové grafiky, bohužel Vás musím zklamat. Vývoji herních, potažmo grafických enginů, jsem se věnoval intenzivně několik let, obhájil jsem na podobné téma diplomovou práci a i si střihnul nějakou tu přednášku na konferenci pro herní vývojáře ;-)
Nečetl jste text pozorně, ve své otázce sem se neptal na definici Javascriptové třídy. Ptal sem se, na definici třídy jako takové. Jelikož zcela přesně víte, co třída není, podělte se prosím o vaši definici.
Podlehl jsem stejnemu pocitu, jako vetsina diskutujicich, ze vsemu rozumim nejvice :) a mam potrebu se vyjadrit jak k clanku, tak k diskusi.
1. K diskusi – vede se tu plamenna debata o tom, jestli ma javascript tridy, o nejakem „klasickem OOP“ a „prototypickem OOP“ a ze jedno ma polymorfismus a druhe ne a podobne nesmysly.
OOP je jedno. Jedna se o obecny pristup, ne o konkretni podobu v nejakem jazyce. Vychazi predevsim ze dvou veci: objektu a jejich chovani – polymorfismu. Nic vic.
Pak jsou tu dva sekundarni principy – zapouzdreni, a dedicnost.
U dedicnosti vidime rozdily, ktere tu zatim nikdo spravne nepojmenoval.
Mame tu tridne-instancni dedicnost (C++, Java, Ruby, Python, SmallTalk, CLISP atd.), kde je dedicnost staticka, definovana mezi tridami. Objekty jsou pouze instance. To je mimochodem princip velmi vzdaleny realite, ale jednoduseji realizovatelny.
Druhy typ je dedicnost prototypicka (self, IO), ktera je dana primo vztahy mezi objekty. Objekt A je potomek objektu B, protoze B je jeho prototyp. To je urcite neco blizsiho realite.
Ano, dedime nektere rysy lidskeho druhu, ktery je zdedil z opice (tridne-instancni pripad). Spis ale dedime konkretni rysy a vlastnosti od nasich rodicu, kteri je zdedili od svych a az nekde hloub byl nejaky predek konkretni opice atd (prototypicky pripad).
Ve skutecnosti jsou tyto dva pripady pouze hranicni body, a konkretni implementace dedicnosti v ruznych jazycich je posunuta k tedne, nebo druhe strane:
C++: Ciste tridne-instancni, tridy jsou pouze definice, pracuje s nimi pouze kompilator, nejsou to objekty.
Instance nejsou vytvareny tridami, ale specialni konstrukci jazyka – operatorem new.
Java: Uz je o krucek jinde. Tridy maji sve zapouzdrujici objekty, umoznujici s nimi „nejak“ pracovat za behu.
Objekty jsou stale vytvareny operatorem new
SmallTalk, Ruby: Tridy uz jsou plnohodnotne objekty. Vytvareji sve instance svymi metodami a ne zadnym spec. operatorem.
JavaScript: Je bliz protoypicke dedicnosti, ale neni to uplna PD. Proc? Protoze prototyp neprirazujeme objektu, ale jeho konstruktoru.
IO, Self: Ciste prototypicka dedicnost – objekty se vytvareji klonovanim existujicich objektu a pridavani dalsich vlastnosti.
Klonovanim vznikne objekt pouze s referenci na prototyp. Nove vlastnosti se ukladaji do nej.
Kdyz se tedy vratim k tomuto problemu v JavaScriptu, tak je videt, ze je napul cesty. Neni ani tridne-instancni, ani striktne prototypicky (prestoze se tak vetsinou oznacuje).
Kdyz v JS vytvarime novy objekt, neni to „z jineho objektu“ ale „nejakym zpusobem“, pomoci funkce oznacene jako konstruktor. Tento princip je stale blizsi tridne-instancnimu modelu a tudiz neni nijak zavadejici, pouzivat pro funkci, kterou pouzivam jako „vytvarejici“, oznaceni Trida. Je to naopak vhodnejsi, nez mast ctenare hned komplikovanou prototypickou dedicnosti, ktera stejne nektere prvky te tridne-instancni obsahuje.
I v ostatnich vecech, ktere jsou clanku vytykany, se vetsinou jedna o omyly. Snad byly vsechny vyjasneny ostatnimi prispevateli.
Osobne si myslim, ze to, co bylo predmetem tohoto dilu clanku, bylo vysvetelno dobre. Jestli je clanek spravne usporadan, to asi kazdy vnima jinak. Myslim, ze pokud ho docte cely, tak uz mu porozumi, a urcite bude lepsi uceleny dojem, az vyjdou i ostatni dily.
Co bych vytkl ja, je krome drobnosti (to je spis doplneni), ze ve scopu najdete i parametry nadrazene funkce (cili zavadeni lokalni promenne, do ktere se tyto ukladaji, je zbytecne a da se pracovat s parametry name primo), predevsim hodnoceni, ze nektere pristupy jsou spatne a nektere dobre.
V teto diskuzi jsou vsichni odbornici provereni praxi, a tak se i ja budu ohanet tou svou :), ve ktere jsem se uz setkal
s nescetne situacemi, kdy byly vyhodne ruzne pristupy k reseni
dedicnosti. At uz vytvareni vzdy novych objektu, jako je to v prvnim spatnem prikladu – mimochodem pokud chcete rozsirovat nektere built-in objekty prenositelne, tak se tomu nevyhnete, tak jsem casto vyuzil uzavery a metody jako jejich rozhrani.
Je to otazka konkretni situace a ne, ze jedno je spatne, a druhe dobre (resp. treti, co bude prezentovano priste).
Na dalsi dily se urcite tesim a i tento prvni hodnotim kladne.
Díky ze vynikající a zevrubný komentář. Jak prototypová dědičnost v Javascriptu přesně funguje, tomu se budu věnovat v druhém článku. A myslím, že vypíchnu jednu charakteristiku, která je naprosto zásadní, ale jenom málokdo si ji uvědomuje.
S tím zdvojením
name
ap_name
máte samozřejmě pravdu,name
není nutné. Zde však slouží jako ilustrace, protože v „tom správném“ příkladě (který bude v druhém díle), se lokálníname
stane instanční vlastností (this.name = name
).K hodnocení že tyto techniky jsou špatné, mělo asi zaznít (velkými písmeny a červeně ;), že jsou špatné pro tvorbu tříd. Ještě přesněji, „zneužití closure“ má význam pouze pro funkcionální programování, v nějakém algoritmu, nebo tam kde potřebuji callback. I v takových případech pak ale nehovoříme o tvorbě tříd. Co se týká techniky druhé, vytváření metod v konstruktoru. Tato technika je dle mého špatná vždy. Respektive nevybavuji si jediné její oprávněné použití. Ale rád se nechám poučit, pokud o nějakém takovém využití víte, sem s ním.
Co se týká dědění vestavěných objektů, které máte na mysli? Funkci podědit nelze. Pole ano, ale nebude správně fungovat v Internet Exploreru. Čísla, řetězce, ani booleany jsem dědit nikdy nezkoušel.
Pokud trida je takova funkce, kterou pouzivame pri vytvareni objektu pomoci operatoru ‚new‘, pak prvni zpusob nejenze je spatne, ale ani nelze pouzit. To mate pravdu.
Na druhou stranu vytvareni objektu pomoci ‚new‘ neni jediny univerzalni zpusob. Kuprikladu DOM objekty muzete vytvaret pouze pomoci funkci, takze pro vytvoreni konkretniho DOM objektu (napr. DIV element s konkretni css tridou a id) budete muset vytvaret necim podobnym, jako je ten prvni priklad.
Druhy priklad se s vyhodou pouzije nejen u callbacku, ale rozhodne pri elegantnim reseni navrhoveho vzoru state, cili implementaci stavoveho polymorfismu.
Rozhodne se mi libi, ze v JS muzete mit stavovy polymorfismus nejen pro konkretni objekt, ale pro celou „tridu“. Ale to uz nesouvisi s uvednymi priklady.
No rozhodně není nutné, abych instanci vytvářel přes new. Existuje tu Factory, což umožňuje některé zajímavé věci v přesunu zodpovědnosti.
První odstavec, souhlas (o DOM elementech ale nebyla řeč, to je jaksi mimo Javascript).
Druhý odstavec, chci vidět kód, stačí ilustrativní. Myslím totiž, že nemáte pravdu. Neznám pattern, jehož implementace by vyžadovala vytváření method v konstruktoru (o tom by druhý špatný příklad). Možná se pletu, ale bez ukázky kódu se nepohneme dál.
Třetí odstavec. Přesouhlasím, viz. druhý díl.
Ano, s tim stavovym polymorfismem pomoci uzaveru jsem se spletl.
muzes Dane prosim rozvest, „zneuziti closure“ pro funkcionalni programovani? Resp. uvest nejaky priklad? Mam na mysli ciste funkcionalni priklad, bojim se, ze pouziti closure jde presne proti jeho duchu, zaklinadlem budiz immutable state a no side effects. Neni mi jasne, k cemu bych v cistem navrhu potreboval videl do scope o uroven vys. Ale to je jen spis takova perlicka na zaver – pro puristy:).
Co třeba toto? Je to v Lispu, ale mělo by to být srozumitelné (násobí to všechny prvky seznamu
coef
), mapcar dělá prakticky totéž co array.each v MooTools nebo jQuery.Do toho mapcaru (each) posílám anonymní funkci, která musí vidět o scope výše, aby se dostala ke
coef
.Diky!
Petře, já tvou otázku neignoruji, jen mám rozečteného Tomáše Petříčka, a chtěl bych svou odpověď čeknou, jen co jej dočtu (někdy dnes;)
Nepovažuji se za odborníka na funkcionální programování, ale mám pocit, že koncept closure se nijak nebije s immutable state. Ani Tomáš Petříček to ve své knize nikde nezmiňuje.
8.2.2 Capturing state using closures in F#
In this section we’re going to talk about closures, which is an important concept in functional programming. Closures are very common and most of the time they aren’t used with mutable state. However, working with mutable state is sometimes needed for the sake of pragmatism and closures give us an excellent way to limit the scope of the mutable state.
(Functional Programming for the Real World)
Docela mě potěšilo, že Tomáš zmiňuje vlastně to samé, co jsem zmiňoval v úvodu svého seriálu. OOP a funkcionální programování nejdou proti sobě. Kdy je tedy správné použít closure? Prostě kdykoliv se mi hodí, aby funkce viděla do scope vyšší úrovně. Dejme tomu, že máme funkci, která využívá jQuery. To, že funkce vidí jQuery z globálního scope je přeci úplně intuitivní, ne? Dále, funkce pracuje s nějakými daty, které jsou definované vně funkce. To, že na ně vidí, je opět velmi praktické. Ale ty chceš příklady, ne kecy, rozumím :)
Tak třeba ten each, přepsaný do Javascriptu:
Lze si představit mnoho obměn, typicky array.sort třeba. Další příklady jsou variantou techniky známé jako currying. V javascriptu ji používáme hodně, tak často, že si to už ani uvědomujeme ;) Většina příkladů na webu jsou totiž podivně nepraktické příklady typu: chci mít funkci, která mi vytvoří funkci, která bude přičítat nějaké číslo. Třeba takto:
K čemu je takový zápis dobrý? K ničemu! ;) Skládat algoritmus z předpřipravených generovaných funkcí je (až na výjimky) cesta do pekla. Tohle je práce pro třídy, dědičnost, agregaci atd.
Nutnost, nebo spíše krásu closure si uvědomíme v okamžiku, kdy píšeme asynchronní kód. Typicky volání serveru, animace, opožděné zobrazení něčeho atd.. díky closure můžeme provázat vyvolání akce s asynchronním obsloužením výsledku, aniž bychom museli vytvářet nějakou novou datovou strukturu, do které bychom vazbu explicitně ukládali.
Díky closure si nemusíme ukládat id někde bokem, případně si jej vracet ze serveru.
Diky, vicemene se tvuj (a ostatnich) pragmatismus ohledne closures shoduje s mym nazorem, byt treba v Erlangu bys asi brzy narazil (ne kazdy jazyk je tak ohebna bestie jako JavaScript). Closure pro mutable state je nicmene vytvareni side effects a „ciste“ funkcionalni programovani to neni. Ale za definice nikdo hlavy utinat nebude. Pragmatismus necht (z)vitezi..;).
Pořád nechápu, co je na tom za side effect.
Pokud pomoci closure ctu neco v odlisnem scope, side effect nevznika (splnena podminka nemennosti). V pripade, ze stav menit budu (kdybych napr. o uroven vys zvysoval counter apod.), side effect vytvarim a purista by o mem kodu rekl, ze neni funkcionalni. Jsou jazyky, kde jsem tohoto pokuseni usetren (promenne jsou automaticky immutable), v nekterych (js) to mozne je (a v nekterych si restrikce urcuju sam – Scala).
Nekdy se to „necista“ praktika zasahu do stavu jineho scope muze hodit – pokud znam dusledky takoveho pouziti, ale obecne je lepsi se ji vyhybat (lze ji vicemene bezpecne pouzit jen tehdy, kdy jsem si jist, ze dany kod pobezi ‚synchronized‘ pouze v jednom procesu v jeden cas).
Ale to uz je debata o necem jinem, diskuzni vlakno vzniklo jen jako reakce na pouziti closures ve funkcionalnim programovani, ktere Dan zminil.
Jo, ale stejnětak může side effect vzniknout bez closure, třeba v párech (Scheme).
Rad bych pripojil tento komentar jako protest ci protivahu vetsinou zjevne nepromyslenych nebo argumenty nepodlozenych komentaru uvedenych vyse. Precetl jsem vsechny tri dily serialu a reaguji tedy na serial jako celek.
Vase clanky splnily (a predcily) ma ocekavani a pomohly mi vyjasnit si nektere aspekty implementace „tridni“ dedicnosti v JavaScriptu. Nesouhlasim s vyse uvedenym nazorem, ze rozdeleni prvnich dvou dilu je nevhodne. Prave naopak si myslim, ze rozdeleni je prinosne i z jinych nez technickych duvodu, nebot dava ctenarum prostor, aby se sami pokusili zamyslet nad korektnim resenim problemu.
Naprosto nesouhlasim s opakovanou „kritikou“ Jiriho Hrebenare a dalsich, zpusobenou bud prilis zbeznym prectenim clanku nebo dokonce nepochopenim prave tech principu, jejich nepochopeni tak radi vytykaji Vam, a zalozenou na velmi zmatene argumentaci. Pokud bych po precteni clanku pochyboval o jeho dobre strukturovanosti a vyborne pochopitelnem stylu, staci pro rozptyleni pochyb porovnat Vase clanky s jim uvedenymi odkazy.
Jeste jednou dekuji a preji hodne inspirace pri dalsi tvorbe.
V té části 2.5.2 „Vylepšené“ třídy nechápu tvrzení, že #nefunguje operátor instanceof,
Animal je typ a proměnná kitty je instancí tohoto typu, tudíž alert(kitty instanceof Animal); vrácí správně true. Jak říkáš, že closure a privilegované metody se pro každou instanci vytváří zas a znova, což je nemalá (a hlavně zbytečná) zátěž, tak níže uvedený zápis je zcela ekvivalentní tvému prototypování, kde uvádíš důkaz, že metody jsou sdílené:
var Animal = function(p_name) {
this.name = p_name;
this.showName = function() {
alert(name);
}
};
var mici = new Animal(‚Mici‘);
var kevi = new Animal(‚Kevi‘);
alert((mici.getName === kevi.getName)); // true
alert((mici.getName === Animal.prototype.getName)); // true
Zajímavé jsou možnosti HTML5:http://www.adobe.com/devnet/html5/articles/javascript-object-creation.html