3. diel - Funkcie a výnimky v CoffeeScript
Vítam vás u tretieho dielu o jazyku, ktorý šetrí programátorov čas a životnosť klávesnice. Minule bola reč a vetvenia a cykloch. Ako som sľúbil, dnes sa budeme rozprávať o veľmi dôležité časti (nielen) JavaScriptu - funkciách a ich zápisu v CoffeeScriptu.
Úvod do funkcií
Ako sme si ukázali už v prvom dieli, funkcie voláme rovnako ako v JS. Môžeme však vynechať zátvorky (ak má funkcie parameter) a bodkočiarka. Ako ale vytvoriť vlastný funkciu? Na začiatok vám musím povedať dôležitú vec - V CoffeeScriptu nemožno deklarovať pomenované funkcie (function declaration), tj. Žiadny kód v CoffeeScriptu sa nezkompiluje do niečoho takého:
function mojeFunkce() { // tělo funkce }
Dôvodom pre vynechanie tohto spôsobu z CoffeeScriptu je podľa autora problém v starších verziách IE (pre viac informácií si prečítajte FAQ). CoffeeScript umožňuje 'iba' ukladať do premenných funkčnej výrazy (function expression). Ak nutne potrebujete mať deklarovanú funkciu, nezúfajte, môžete jednoducho dať bežný JS kód medzi `` (na anglickej klávesnici znak pod Esc):
# Coffee kód... `function mojeJSFunkce(x) { var vysledek = x / 2; return vysledek; }` # Coffee kód...
Tieto znaky povedia CoffeeScriptu "Toto nemusíš prekladať, je to normálne JS". Možno som sa mal o tejto vychytávke zmieniť už na začiatku seriálu
Späť k funkciám v CoffeeScriptu. Syntax je nasledujúca (pozor na odsadenie tela funkcie):
funkce = ->
7
JavaScript:
var funkce; funkce = function() { return 7; };
Všimnite si zaujímavé veci - v CoffeeScript kóde som nenapísal
slovíčko return
, vo výslednom JS kóde sa však objavilo. V
CoffeeScriptu funkcie vždy vracia posledný príkaz. Ak chcete
tomuto správaniu zabrániť, stačí jednoducho napísať:
funkce = -> console.log 'Nic nevracím' return
Najskôr vás napadlo, ako sa CoffeeScript zachová, keď budeme mať vo funkcii vetvenia.
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ýsledok:
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 vracia nielen posledný príkaz celej funkcie, ale tiež posledný príkaz vo vetve.
Funkcie s parametrami, IIFE & splats
Funkcia samozrejme existujú preto, aby spracovali dáta, ktoré im odovzdáme - parametre. Zápis funkcie s dvoma parametrami vyzerá v CoffeeScriptu nasledovne:
funkce = (x, y) -> x + y
Parametrom môžete jednoducho nastaviť predvolené hodnoty (ako napr. 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);
Iste viete, že JS umožňuje bezprostredne vyvolať funkčné výraz (immediately Invoked Function Expression - IIFE) nasledovne:
(function() {return alert('bububu');})();
V CoffeeScriptu možno IIFE zapísať podobne:
(-> alert 'bububu')()
Ale keďže už poznáme slovo do
, ktoré volá funkciu,
nahradíme ním zátvorky:
do -> alert 'bububu'
V CoffeeScriptu môžeme pomocou tzv. Splats (znamená "plácnutí") jednoducho vytvoriť funkciu, ktorá prijme ľubovoľný počet parametrov:
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šimnite si, že splat...
možno použiť aj pri volaní
funkcie a to spôsobí, že jednotlivé položky poľa sa berú ako samostatné
parametre, namiesto aby sa celé pole považovalo za jeden parameter, ako je
tomu u pole [ 'mam', 'rád ',' kavu ']. Vo výslednom JS kódu vidíme, že
splats sú riešené pomocou 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'])));
Povedzme, že budeme chcieť vypísať posledný argument pomocou funkcie alert (), jednoducho pridáme ďalší parameter:
funkce = (parametry..., posledni) -> console.log parametr for parametr in parametry alert "#{posledni} <- tohle je poslední parametr"
Keď použijeme rovnaké pole ako v minulom príklade, posledná hodnota v konzole bude "Tchibo" a vypíše sa nám hláška "Starbucks <- toto je posledná parameter".
Premenné v rôznom rozsahu (scope)
Vám, ktorí v JS programujete už nejaký ten piatok, možno vŕtajú hlavou
otázky, ako: "Keď nemôžem deklarovať premennú pomocou var
,
aké správanie mám očakávať, keď mám vo funkcii a mimo nej premennú s
rovnakým menom?", Alebo: "Ako definujú globálnej premennej? ".
Odpoveď na prvú otázku: Ak vo funkcii bude premenná s rovnakým názvom, ako mimo nej, nebude deklarovať lokálne premennú (parametre s rovnakým menom ako vonkajšia premenná sú samozrejme formálne). Toto je rozdiel oproti napr. 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ávajte si teda pozor, obzvlášť v prípadoch, kedy budete mať niekoľko funkcií vnorených do seba.
Odpoveď na druhú otázku: Musíte ju explicitne priradiť k najvyššiemu objektu v hierarchii (obyčajne window). Ak ste si nainštalovali CoffeeScript, iste ste si všimli, že všetok kompilovaný kód sa vkladá do anonymný funkcie
(function(){ // vygenerovaný kód })();
Vďaka týmto vlastnostiam je veľmi ťažké znečisťovať omylom globálny
menný priestor, na rozdiel od bežného JS, kde jednoducho zabudnete
var
a už to ide.
Výnimky
Syntax výnimiek je takmer totožná s tou v JavaScripte, akurát názov
výnimky sa nemusia špecifikovať a je vždy pomenovaný
_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 bolo pre tento diel všetko a tešte sa na ten ďalší, budeme totiž preberať OOP.