3. díl - Funkce a výjimky v CoffeeScript

JavaScript CoffeeScript Funkce a výjimky v CoffeeScript

Vítám vás u třetího dílu o jazyce, který šetří programátorův čas a životnost klávesnice. Minule byla řeč a větvení a cyklech. 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 a těšte se na ten další, budeme totiž probírat OOP.


 

  Aktivity (1)

Článek pro vás napsal Yahkem
Avatar

Jak se ti líbí článek?
Celkem (4 hlasů) :
4.754.754.754.754.75


 


Miniatura
Předchozí článek
Větvení a cykly v CoffeeScriptu
Miniatura
Všechny články v sekci
CoffeeScript

 

 

Komentáře

Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:

Příjemný způsob výkladu a nahuštěno informacemi, velmi povedené články :)

Odpovědět  +1 28.7.2014 19:20
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 1 zpráv z 1.