Předmluva – proč se o CoffeeScript zajímat?
Když jsem se sám učil JavaScript, připadal mi nejprve jako špatně udělaná nápodoba C: syntaxe je člověku povědomá, ale spousta věcí mu připadá nedotažených či nesmyslných. Touto fází si projde snad každý, kdo zná C-like jazyky a setká se s JavaScriptem. Ostatně i v komentářích na Zdrojáku se lze často setkat s lidmi, kteří na JavaScript velmi urputně nadávají. Připadá jim, že JavaScript je neschopný spatlaný jazyk, co vyzývá svou „nepořádností“ k bastlení kódu a podporuje programátorská neřádstva. Pokud nedáte JavaScriptu druhou šanci, dost možná na této úrovni poznání zůstanete a každé setkání s ním bude pro vás utrpením (že musíte používat takovou hrůzu, zlatá Java, zlaté C++, …)

Při druhém přiblížení, po zkušenosti s jQuery a Prototype, jsem se naučil simulovat třídy funkcemi. JavaScript mi v tu chvíli připadal jako celkem přijatelný jazyk, i když poměrně výstřední (čti: z nějakého důvodu nedodržující mainstreamové zvyklosti). A pak jsem si jednoho dne uvědomil, že se na JavaScript pořád dívám ze špatného směru – jako na OO skriptovací nástroj, jako na bratrance skriptů v BASHi, v perlu, v PHP… Stačilo se podívat z jiného směru, třeba od Lispu (co jsem od konce 80. let nepoužil). A pak jsem to uviděl a vše do sebe začalo zapadat…
Jsem přesvědčen o tom, že pokud budete na JavaScript hledět jen jako na objektově orientovaný skriptovací jazyk, bude vám stále připadat nedomyšlený a nesmyslný. A stejně tak věřím, že CoffeeScript může vnímavému člověku právě ve změně pohledu pomoci. I proto vznikl tento miniseriál, který si klade za cíl představit zajímavý nástroj, postavený nad JavaScriptem, a ukázat tak možnosti samotného JS, které zůstávají při běžném pohledu skryté.
Úvod
Na co návrháři JavaScriptu mysleli? Jako vážně – když už navrhujete jazyk, který má funkce prvního řádu, proč je v něm, proboha, potřeba psát klíčové slovo
píše v nadsázce Piers Cawley. Jeho slova jako by vyslyšel Jeremy Ashkenas a téměř na den přesně před rokem (13. prosince 2009) publikoval na GitHubu první verzi svého jazyka s komentářem „Initial commit of the mystery language.“ Ze záhadného jazyka se během roku vývoje stal CoffeeScript (zkraťme si jej na CfS), dospěl do verze 0.9.5 a na Vánoce je oznámena první „ostrá“ verze (1.0).function
? Lisp je zaflákaný slovem lambda
, ale to je proto, že to je zkrátka starý jazyk a nic lepšího je tehdy nenapadlo. Asi i proto mají ty makra, která mají – cokoli, jen aby nemuseli psát stále dokola lambda.
CoffeeScript je, prakticky vzato, nadstavba nad JavaScriptem, která uživatelům JS přináší spoustu „syntaktického cukru“, inspirovaného Pythonem a Ruby. Nabízí čistší funkcionální zápis a zajišťuje, že výsledný kód používá správné konstrukce. Zápis může připadat někomu překomplikovaný, jinému naopak krásně čistý… Do které skupiny patříte, si zjistíte jednoduchým testem:
dvojmoc = (x) -> x * x soucet = (xs) -> r = 0 (r = r + x) for x in xs r
Pokud vám kód připadá jasný, logický a pochopitelný, je CoffeeScript přesně pro vás!
Použití CoffeeScriptu
Překladač CoffeeScriptu je běžný JavaScriptový program. (Ve skutečnosti je od verze 0.5 napsán sám v CoffeeScriptu a „přeložen sám sebou“ do JS.) Můžete jej použít dvěma způsoby – buď jej můžete spustit v prostředí Node jako běžnou aplikaci pro příkazový řádek (instalace pomocí npm install coffee-script
), nebo jej můžete spouštět přímo v prohlížeči. V prohlížeči jej můžete spustit jednak jako klasický překladač, který vezme zdrojový text a vrátí výsledek (funkce CoffeeScript.compile()
), jednak jej můžete použít v běžném tagu script
s typem nastaveným na text/coffeescript
. (V tomto případě je ale důležité si uvědomit, že skripty budou provedeny v jiném kontextu než kdyby byly přímo zapsány v tagu script; nejen kvůli tomu se takové použití nedoporučuje – i když je možné.)
Překladač CfS můžete otestovat na stránkách CoffeeScriptu (pod odkazem TRY COFFEESCRIPT). V překladači vidíte i použití pomocí text/coffeescript, i spolupráci s jinými knihovnami.
Základy CoffeeScriptu
Ne, nebudeme zabíhat do úplných základů programování, předpokládáme, že čtenáři programovat umí, jen je zajímá, co nového a jiného na ně CfS má. Výklad vychází z Language Reference, kde si můžete příklady rovnou i vyzkoušet.
Zápis bloků a funkcí
CoffeeScript se inspiroval u Pythonu a používá stejný způsob zápisu bloků pomocí odsazení. Úvodní mezery na řádku jsou tedy důležité, označují zanoření bloku. CfS nemá složené závorky k označování bloků, místo nich použijte odsazení. Stejně tak nemusíte psát středníky za příkazem, výrazem či funkcí; středník použijete pouze v případě, že chcete na jednom řádku uvést např. víc volání funkcí.
U volání funkce s parametry není nutné použít závorky – parametry prostě následují za jménem funkce: print "Ahoj"
a pokračují až do konce řádku nebo bloku. Volání funkce bez parametrů je potřeba zapsat se závorkami.
Funkce
Funkce jsou definovány jednoduše, viz příklad výše: zadáte seznam parametrů, šipku a tělo funkce.
dvojmoc = (x) -> x * x trojmoc = (x) -> x * dvojmoc x # jiná možnost: trojmoc = (x) -> dvojmoc(x) * x # zkuste si, co by se stalo, kdybyste vynechali závorky u dvojmoc(x)...
Parametrům funkce můžete nastavit i výchozí hodnotu – zápis je očekávatelný:
nalij = (co, kam = "do sklenice") -> "Nalil jsi #{co} #{kam}" nalij "pivo"
Oblast platnosti proměnných
Proměnné jsou v CfS vždy definované v tom bloku, kde jsou použité. Pokud existuje už proměnná stejného jména v nadřízeném bloku, je použita ta. Příklad:
vnejsi = 1 funkce = -> vnitrni = 2 vnejsi = 3 vnitrni = funkce()
Po překladu získáme tento kód:
var funkce, vnejsi, vnitrni; vnejsi = 1; funkce = function() { var vnitrni; vnitrni = 2; return vnejsi = 3; }; vnitrni = funkce();
Vidíme, že ve funkci není proměnná vnejsi deklarována jako lokální, ale je místo toho použita globální. Proto buďte opatrní při psaní vnořených funkcí, abyste nepoužili jméno proměnné z nadřazeného bloku.
Řetězce
Řetězce se v CfS zapisují jako v jiných jazycích, tj. do uvozovek či apostrofů. Podobně jako v PHP zde platí, že řetězec zapsaný v apostrofech je konstantní, v řetězci zapsaném v uvozovkách proběhne nahrazení proměnných a výpočet výrazů, pokud jsou zapsané jako #{...}
.
"Třikrát pět je #{3 * 5}"
Řetězce mohou být zapsány přes více řádků, pokud jsou správně odsazeny:
lipsum = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
Pokud chcete zapsat řetězec včetně odsazení, apostrofů i uvozovek, můžete použít syntaxi heredoc:
html = ''' <strong> CoffeeScript! </strong> '''
Pokud místo apostrofů použijete uvozovky, bude fungovat doplňování výrazů, viz výše.
Podobným zápisem můžete vložit do textu i víceřádkový komentář – ten zůstane i ve výsledném kódu. Je tedy ideální např. pro vložení licenčních informací na začátek kódu:
### CoffeeScript Compiler v0.9.5 Released under the MIT License ###

Vše je výraz
Možná jste si všimli, že v předchozích příkladech v definicích funkcí nebyl použit příkaz return
. Funkce zkrátka vrátí hodnotu posledního vyhodnoceného výrazu. Přitom výrazem může být téměř cokoli – výraz v běžném slova smyslu, přiřazení nebo příkaz.
Pokud použijeme přiřazení coby výraz, postará se překladač CfS o to, aby proměnné byly správně deklarovány, i když jsou použity prvně:
sest = (jedna = 1) + (dva = 2) + (tri = 3)
Výsledný JavaScript vypadá takto:
var dva, jedna, sest, tri; sest = (jedna = 1) + (dva = 2) + (tri = 3);
Blok příkazů je taky výraz, hodnotou je hodnota posledního výrazu v něm.
CoffeeScript je principem „vše je výraz“ prolezlý skrznaskrz, takže i věci, které by v běžném Javascriptu byly příkazy, dokáže zpracovat jako výraz – zabalením do closure. (S výjimkami tam, kde to nemá smysl, např. break či continue.) S příklady se ještě setkáme. Na jednu stranu lze tak dělat poměrně kuriózní konstrukce, jako např. použít blok try-catch
coby výraz, ale lze to využít i šikovně – například použít foreach na místě pole:
globals = (name for name of window)[0...10]
Objekty a pole
Zápis objektových literálů či polí je podobný JSON – tedy objekty se zapisují do složených závorek, pole do hranatých.
pole = [1, 1, 2, 3, 5, 8] bod = {x: 10, y: 20}
Pole může být (s odsazením) i přes více řádků, objekt zase může být zapsán v notifikaci podobné YAMLu:
ctverec = [ 1, 1, 1 1, 0, 1 1, 1, 1 ] znalosti = php: uroven: "pokrocily" praxe: 8 html: uroven: "zacatecnik" praxe: 3
Příjemná vlastnost CfS je, že nemusíte dávat pozor na klíčová slova. V JavaScriptu nelze zapsat vlastnost objektu, pokud se jmenuje stejně jako klíčové slovo (třeba „class“), přímo, musí být v uvozovkách. jQuery konstrukci
$('#element').attr({"class":"hidden"});
zapíšeme v CoffeeScriptu jako
$('#element').attr class: 'hidden'
Všimněte si, že lze bez problémů používat i funkce z existujících knihoven, spolupráce s jQuery a podobnými je v CfS naprosto přirozená a bezproblémová.
Jazykové konstrukce

Jazykové konstrukce CoffeeScriptu nepřinášejí žádné zásadní novinky oproti JavaScriptu – jen spoustu syntaktického cukru inspirovaného jinými jazyky (Ruby, Python), díky němuž se kód blíží běžné angličtině.
Podmínky
Příkaz if lze zapisovat bez závorek okolo podmínky a bez složených závorek okolo bloku (víceřádkové bloky se opět vyznačují pomocí odsazení). Lze jej zapsat i v postfixové notaci, tedy až za příkaz. Lze jej použít (viz předchozí pasáž „Vše je výraz“) i jako výraz, který je přeložen pomocí ternárního operátoru ?:
.
vezmi kulich if zima #alternativa s unless - význam jako if not... vezmi kulich unless teplo if prsi destnik = 1 plastenka = 1 else slunecniBryle = true auto = if penize then "Mercedes" else "Žádné"
Slovo „then“ umožňuje oddělit podmínku od hodnoty v případě, že chceme vše zapsat na jeden řádek.
CoffeeScript umožňuje použití zřetězených podmínek, tedy například:
optimum = 18 < teplota < 23
Podmíněné přiřazení a existenční operátor
Hodí se všude tam, kde chceme proměnné přiřadit hodnotu, pokud ještě žádnou nemá (nebo má nulovou):
options or= defaults
Výsledkem bude výraz options || (options = defaults);
Obdobně lze použít i operátor and=
.
Předchozí přiřazení proběhne i v případě, že proměnná options obsahuje nulu (false, null). Pokud chceme otestovat, že proměnné nebyla přiřazena žádná hodnota, musíme použít existenční operátor proměnná?
. Můžeme tedy napsat
settings = auto if settings? and not auto?
a výsledkem bude JavaScriptový zápis:
var settings; if ((typeof settings != "undefined" && settings !== null) && !(typeof auto != "undefined" && auto !== null)) { settings = auto; }
Podobně můžeme existenční operátor použít s přiřazením:
odpoved ?= 42
Překlad do JavaScriptu odpovídá známé konstrukci:
typeof odpoved != "undefined" && odpoved !== null ? odpoved : odpoved = 42;
Podobně se lze vyhnout i chybě při přístupu k nedefinovaným vlastnostem objektu, a to operátorem ?.
– příklad:
pozice = window?.geolocation?.position
a výsledek:
var pozice, _ref; pozice = typeof window != "undefined" && window !== null ? (_ref = window.geolocation) != null ? _ref.position : void 0 : void 0;
Aliasy
CoffeeScript se snaží vyjít programátorům vstříc a udělat ze čtení kódu příjemný zážitek. Nabízí proto další syntaktický cukr v podobě nejrůznějších aliasů.
Už jsme viděli, že operátor && lze nahradit slovem and
a operátor || slovem or
. Podobně lze negaci (!) zapsat jako not
. Operátor porovnání (==) můžeme nahradit slovem is
, operátor nonekvivalence slovem isnt
. (CoffeeScript překládá ==
a is
jako „===“, !=
a isnt
jako „!==“.)
U logických hodnot platí, že true můžeme zapsat i jako on
, popřípadě jako yes
. False pak analogicky jako off
, nebo no
.
Pro přístup k vlastnostem this
můžeme použít zápis pomocí @ – tedy this.value
můžeme zapsat jako @value
. Více si o objektech povíme v následujícím dílu.
Ukázka použití aliasů:
show button if checkbox is on if rychlost < limit then pridej()
Operátor is lze použít i v konstrukci, v níž zjišťujeme, zda je prvek obsažen v poli hodnot:
colorOK = true if color in ['red', 'green', 'blue']
Smyčky
CoffeeScript nabízí několik typů smyček. Tou základní je while
– od své jmenovkyně v JavaScriptu se liší tím, že je výrazem, jehož hodnotou je pole, které obsahuje výsledky (= poslední vyhodnocený výraz v těle smyčky) pro každý průchod. Tedy například:
pocet = 5 odpocitavani = while pocet-- "Zbyvajicich dni: #{pocet}..."
Opět máme k dispozici aliasy: until
je ekvivalent pro while not
, loop
pro while(true)
(nezapomeňte včas ze smyčky vyskočit). Opět je možná postfixová notace jako u if:
read file until EOF
Výsledek je, jak už víme, pole posledních hodnot pro jednotlivé průchody, takže například emulace funkce file()
z PHP, která čte jednotlivé řádky souboru a vrací je coby položky pole, by mohla vypadat teoreticky takto:
radky = (read file until EOF)
For…?
Možná se ptáte, kde je smyčka for či foreach… CfS se tu inspiroval u Pythonu a podobné situace řeší pomocí procházení pole, objektu či rozsahu. Syntax je jednotná: for proměnná in objekt (pole, rozsah)
alert hlaska for hlaska in seznam r = r + i for i in [0..9]
Smyčka for je opět výraz – pole výsledků jednotlivých iterací:
odpocitavani = ("Zbyvajicich dni: #{pocet}..." for pocet in [5..1])
Bez závorek bude výsledek jiný – podívejte se, v čem a proč (a zjistíte, že je to logické).
Pomocí operátoru in
procházíme hodnoty pole či rozsahu. Chceme-li projít vlastnosti objektu, použijeme operátor of
:
lidi = petr: 31, pavel: 29, tomas: 22 alert (jmeno for jmeno of lidi) alert (jmeno + ":" + rok for jmeno, rok of lidi)
Pokud se podíváte na výsledný kód, vidíte, že CoffeeScript ošetřuje přístup pomocí hasOwnProperty.
var jmeno, lidi, rok, _results, _results2; var __hasProp = Object.prototype.hasOwnProperty; lidi = { petr: 31, pavel: 29, tomas: 22 }; alert((function() { _results = []; for (jmeno in lidi) { if (!__hasProp.call(lidi, jmeno)) continue; _results.push(jmeno); } return _results; }())); alert((function() { _results2 = []; for (jmeno in lidi) { if (!__hasProp.call(lidi, jmeno)) continue; rok = lidi[jmeno]; _results2.push(jmeno + ":" + rok); } return _results2; }()));
Pokud z jakéhokoli důvodu chcete použít opravdu „syrovou“ verzi JavaScriptového for (key in ...)
, zapište ji jako for all key, value of ...
Pokračování příště…
V příštím dílu se podíváme na zoubek objektům, třídám, dědičnosti a dalším věcem s tímto tématem spojeným. Ve třetím, posledním, se pak podíváme na některé zajímavosti, spojené s funkcemi vyšších řádů (higher order functions), a na to, jak nám s jejich použitím CoffeeScript pomáhá (ano, ony jsou i v JavaScriptu, ale jsou utopené v Lisp Revenge, tedy v nekonečném počtu závorek). Ukážeme si i některé příklady funkcionálních optimalizací, a pokud do té doby vyjde verze 1.0, tak se samosebou podíváme i na ni.
Pokud máte po dnešním představení CoffeeScriptu dojem, že jde jen o další módní vlnu a zoufalou snahu udělat z JavaScriptu použitelný jazyk, ale nenabízí nic víc než syntaktický cukr, splácaný z několika dalších jazyků, počkejte si na další díly. Budeme sladit JavaScript ještě víc a uvidíte pár věcí, co v C++ ani v Javě nenaprogramujete s takovou elegancí jako v CfS.
Upozornění: Mějte na paměti, že větší volnost znamená i větší zodpovědnost!
Dodatek – CoffeeScript v praxi
A používá to vůbec někdo? Dobrá otázka. Několik příkladů by se našlo: V CoffeeScriptu byl napsán web BusyConf. Používá jej Ars Technica pro svou čtečku pro iPad. Použili ho 37signals pro aplikaci Chalk (zdrojový kód zde – můžete ochutnat, jak taková aplikace vypadá, a srovnat to s odpovídajícím JavaScriptem). Plus desítky malých aplikací, webů a ukázek, u nichž se autoři zmínili, že je v CfS napsali.
Správná otázka není, zda to někdo používá, ale co nám to přinese. A tady už je odpověď zajímavější: CoffeeScript nabízí oproti JavaScriptu několik velmi pěkných výhod – píšete čistší a menší kód (až o třetinu míň textu) bez závorkového pekla. Máte k dispozici „syntaktický cukr“ včetně např. tříd či syntaxe heredoc. Kód je srozumitelnější, a tedy i snáze udržovatelný. Zásadním problémem je debugování – případnou chybu budete hledat kdesi v (naštěstí čitelném) JavaScriptu, a pak budete muset přijít na to, která pasáž ve zdrojovém CfS kódu odpovídá tomu místu. Je to výzva do verze 1.0.
Stay tuned.
Ilustrace: Open Clipart Library
Přehled komentářů