Lekce 3 - Funkce a výjimky v CoffeeScript
Minule, v lekci Větvení a cykly v CoffeeScriptu, byla řeč a větvení a cyklech.
Vítám vás u dalšího dílu o jazyce, který šetří programátorův čas a životnost klávesnice. Jak jsem slíbil, dnes si budeme povídat o velmi důležité části (nejen) JavaScriptu - funkcích a jejich zápisu v CoffeeScriptu.
Úvod do funkcí
Jak jsme si ukázali již v prvním dílu, funkce voláme stejně jako v JS. Můžeme však vynechat závorky (pokud má funkce parametr) a středník. Jak ale vytvořit vlastní funkci? Na začátek vám musím říct důležitou věc - V CoffeeScriptu nelze deklarovat pojmenované funkce (function declaration), tj. žádný kód v CoffeeScriptu se nezkompiluje do něčeho takového:
function mojeFunkce() { // tělo funkce }
Důvodem pro vynechání tohoto způsobu z CoffeeScriptu je podle autora
problém ve starších verzích IE (pro více informací si přečtěte FAQ).
CoffeeScript umožňuje 'pouze' ukládat do proměnných funkční výrazy
(function expression). Pokud nutně potřebujete mít deklarovanou funkci,
nezoufejte, můžete jednoduše dát běžný JS kód mezi
`
`
(na anglické klávesnici znak pod
Esc):
# Coffee kód... `function mojeJSFunkce(x) { var vysledek = x / 2; return vysledek; }` # Coffee kód...
Tyto znaky řeknou CoffeeScriptu „Toto nemusíš překládat, je to normální JS“. Možná jsem se měl o této vychytávce zmínit již na začátku seriálu
Zpátky k funkcím v CoffeeScriptu. Syntaxe je následující (pozor na odsazení těla funkce):
funkce = ->
7
JavaScript:
var funkce; funkce = function() { return 7; };
Všimněte si zajímavé věci - v CoffeeScript kódu jsem nenapsal
slovíčko return
, ve výsledném JS kódu se však objevilo. V
CoffeeScriptu funkce vždy vrací poslední příkaz. Jestliže
chcete tomuto chování zabránit, stačí jednoduše napsat:
funkce = -> console.log 'Nic nevracím' return
Nejspíše vás napadlo, jak se CoffeeScript zachová, když budeme mít ve funkci větvení.
funkce = -> r = Math.floor Math.random() * 10 + 1; if r < 5 'menší, než 5' else if r > 5 'větší, než 5' else 5
Výsledek:
var funkce; funkce = function() { var r; r = Math.floor(Math.random() * 10 + 1); if (r < 5) { return 'menší, než 5'; } else if (r > 5) { return 'větší, než 5'; } else { return 5; } };
Vidíme, že CoffeeScript vrací nejen poslední příkaz celé funkce, ale také poslední příkaz ve větvi.
Funkce s parametry, IIFE & splats
Funkce samozřejmě existují proto, aby zpracovávaly data, která jim předáme - parametry. Zápis funkce s dvěma parametry vypadá v CoffeeScriptu následovně:
funkce = (x, y) -> x + y
Parametrům můžete jednoduše nastavit výchozí hodnoty (jako např. v PHP):
funkce = (cislo = 10) -> alert "Číslo je #{cislo}" funkce() # Číslo je 10 funkce 30 # Číslo je 30
JavaScript:
var funkce; funkce = function(cislo) { if (cislo == null) { cislo = 10; } return alert("Číslo je " + cislo); }; funkce(); funkce(30);
Jistě víte, že JS umožňuje bezprostředně vyvolat funkční výraz (Immediately Invoked Function Expression – IIFE) následujícím způsobem:
(function() {return alert('bububu');})();
V CoffeeScriptu lze IIFE zapsat podobně:
(-> alert 'bububu')()
Ale jelikož již známe slovo do
, které volá funkci,
nahradíme jím závorky:
do -> alert 'bububu'
V CoffeeScriptu můžeme pomocí tzv. splats (znamená „plácnutí“) jednoduše vytvořit funkci, která přijme libovolný počet parametrů:
funkce = (parametry...) -> console.log parametr for parametr in parametry funkce(1, 2, 'tri', ['mam', 'rad', 'kavu'], ['Nescafé', 'Tchibo', 'Starbucks']...) # 1 # 2 # tri # ["mam", "rad", "kavu"] # Nescafé # Tchibo # Starbucks
Všimněte si, že splat...
lze použít i při volání funkce
a to způsobí, že jednotlivé položky pole se berou jako samostatné
parametry, místo aby se celé pole považovalo za jeden parametr, jako je tomu
u pole ['mam', 'rad', 'kavu']. Ve výsledném JS kódu vidíme, že splats jsou
řešeny pomocí objektu arguments:
var funkce, __slice = [].slice; funkce = function() { var parametr, parametry, _i, _len, _results; parametry = 1 <= arguments.length ? __slice.call(arguments, 0) : []; _results = []; for (_i = 0, _len = parametry.length; _i < _len; _i++) { parametr = parametry[_i]; _results.push(console.log(parametr)); } return _results; }; funkce.apply(null, [1, 2, 'tri', ['mam', 'rad', 'kafe']].concat(__slice.call(['Nescafé', 'Tchibo', 'Starbucks'])));
Řekněme, že budeme chtít vypsat poslední argument pomocí funkce alert(), jednoduše přidáme další parametr:
funkce = (parametry..., posledni) -> console.log parametr for parametr in parametry alert "#{posledni} <- tohle je poslední parametr"
Když použijeme stejné pole jako v minulém příkladu, poslední hodnota v konzoli bude "Tchibo" a vypíše se nám hláška "Starbucks <- tohle je poslední parametr".
Proměnné v různém rozsahu (scope)
Vám, kteří v JS programujete již nějaký ten pátek, možná vrtají
hlavou otázky, jako: „Když nemohu deklarovat proměnnou pomocí
var
, jaké chování mám očekávat, když mám ve funkci a mimo
ní proměnnou se stejným jménem?“, nebo: „Jak definuji globální
proměnnou?“.
Odpověď na první otázku: Jestliže ve funkci bude proměnná se stejným názvem, jako mimo ní, nebude deklarovat lokální proměnnou (parametry se stejným jménem jako vnější proměnná jsou samozřejmě formální). Toto je rozdíl oproti např. PHP:
$x = 1; function f() { $x = 5; } echo $x; # 1 f(); echo $x; # 1
CoffeeScript:
x = 1 f = -> x = 5 alert x # 1 f() alert x # 5
Dávejte si tedy pozor, obzvlášť v případech, kdy budete mít několik funkcí vnořených do sebe.
Odpověď na druhou otázku: Musíte ji explicitně přiřadit k nejvyššímu objektu v hierarchii (obyčejně window). Jestliže jste si nainstalovali CoffeeScript, jistě jste si všimli, že všechen kompilovaný kód se vkládá do anonymní funkce
(function(){ // vygenerovaný kód })();
Díky těmto vlastnostem je velmi těžké znečišťovat omylem globální
jmenný prostor, na rozdíl od běžného JS, kde prostě zapomenete
var
a již to jede.
Výjimky
Syntaxe výjimek je téměř totožná s tou v JavaScriptu, akorát název
výjimky se nemusí specifikovat a je vždy pojmenován _error
:
f = (x) -> if x is undefined throw 'Houstone, máme problém' else alert x try f() catch alert _error finally alert 'Konec'
JavaScript:
var f; f = function(x) { if (x === void 0) { throw 'Houstone, máme problém'; } else { return alert(x); } }; try { f(); } catch (_error) { alert(_error); } finally { alert('Konec'); }
To by bylo pro tento díl vše.
V další lekci, Objektově orientované programování v CoffeeScript, se budeme věnovat OOP v CoffeeScriptu.