Top 11 (a více!) funkcí JavaScriptu, které musíte znát

Efektivní programování v JavaScriptu

Zdokonalte své dovednosti v JavaScriptu a staňte se rychlejším, produktivnějším a spokojenějším vývojářem. Osvojte si klíčové funkce, které se v tomto jazyce často opakují.

JavaScript se nachází všude, ať už se jedná o backend, frontend, či dokonce kosmické aplikace. Tento jazyk je velmi flexibilní, nabízí funkcionální programovací vzory i klasické třídy. Díky podobnosti s jazyky „typu C“ je pro vývojáře snadné přejít z jiných prostředí.

Pokud chcete posunout své dovednosti v JS na vyšší úroveň, doporučujeme se seznámit, procvičit a ovládnout následující základní funkce. Ne všechny jsou nezbytné pro řešení problémů, ale mohou usnadnit složitou práci nebo zkrátit kód.

Funkce map()

Článek o důležitých funkcích JavaScriptu se neobejde bez zmínky o `map()`. Spolu s `filter()` a `reduce()` tvoří základní trojici, kterou budete používat opakovaně během své kariéry. Je tedy dobré se na ně podívat blíže.

Funkce `map()` patří mezi ty, které začátečníkům v JavaScriptu dělají největší potíže. Ne proto, že by byla složitá, ale protože její princip vychází z funkcionálního programování. Naše myšlení je více orientované na objektové programování, a proto nám může princip fungování této funkce připadat zvláštní.

JavaScript je více funkcionální než objektově orientovaný jazyk, ačkoli moderní verze se to snaží skrývat. Ale to je jiné téma. 🤣 Tak tedy, funkce `map()`.

`map()` je jednoduchá funkce, která se aplikuje na pole a transformuje každý prvek na něco jiného, čímž vytváří nové pole. Konkrétní transformace je definována pomocí anonymní funkce.

To je vše! Syntax může vyžadovat trochu zvyku, ale v podstatě to je to, co `map()` dělá. K čemu ji můžeme použít? Záleží na tom, čeho se snažíme dosáhnout. Řekněme například, že máme zaznamenané denní teploty z minulého týdne v poli. Nyní však víme, že přístroje nebyly přesné a udávaly o 1,5 stupně nižší teplotu.

Tuto korekci můžeme provést pomocí `map()` takto:

 const weeklyReadings = [20, 22, 20.5, 19, 21, 21.5, 23];
 const correctedWeeklyReadings = weeklyReadings.map(reading => reading + 1.5);
 console.log(correctedWeeklyReadings); // výstup: [ 21.5, 23.5, 22, 20.5, 22.5, 23, 24.5 ]
 

Dalším praktickým příkladem je použití v Reactu, kde se často z polí vytvářejí seznamy prvků DOM. Běžně se setkáme s tímto kódem:

 export default ({ products }) => {
  return products.map(product => {
   return (
    <div className="product" key={product.id}>
     <div className="p-name">{product.name}</div>
     <div className="p-desc">{product.description}</div>
    </div>
   );
  });
 };
 

Zde máme funkční komponentu React, která jako rekvizitu dostává seznam produktů. Z tohoto seznamu (pole) vytváří seznam HTML „div“, čímž se každý objekt produktu transformuje na HTML. Původní objekt produktů zůstává nezměněn.

Můžete namítnout, že `map()` je jen vylepšený `for` cyklus, a měli byste pravdu. Ale jakmile tento argument vyslovíte, je to vaše objektově orientované myšlení. Tyto funkce a jejich použití pocházejí z funkcionálního programování, kde je uniformita, kompaktnost a elegance vysoce ceněna. 🙂

Funkce filter()

`filter()` je velmi užitečná funkce, kterou budete často používat. Jak název napovídá, tato funkce filtruje pole podle zadaných pravidel/logiky a vrací nové pole obsahující pouze prvky, které tato pravidla splňují.

Vraťme se k našemu příkladu s počasím. Mějme pole s maximálními teplotami z minulého týdne. Nyní chceme zjistit, kolik dní bylo chladnějších. Chladnější je subjektivní pojem, takže řekněme, že hledáme dny s teplotou nižší než 20. To můžeme udělat pomocí `filter()` takto:

 const weeklyReadings = [20, 22, 20.5, 19, 21, 21.5, 23];
 const colderDays = weeklyReadings.filter(dayTemperature => {
  return dayTemperature < 20;
 });
 console.log("Počet chladnějších dnů v týdnu: " + colderDays.length); // 1
 

Všimněte si, že anonymní funkce, kterou předáváme `filter()`, musí vracet booleovskou hodnotu: true nebo false. To umožní `filter()` zjistit, zda má daný prvek zahrnout do filtrovaného pole. Do této anonymní funkce můžete umístit libovolnou logiku, volat API, číst vstupy uživatele, ale vždy se musíte ujistit, že na konci vracíte booleovskou hodnotu.

Upozornění: Z vlastní zkušenosti vývojáře JavaScriptu musím zmínit, že mnoho programátorů dělá chyby při použití `filter()`. Přepišme předchozí kód tak, aby obsahoval chybu:

 const weeklyReadings = [20, 22, 20.5, 19, 21, 21.5, 23];
 const colderDays = weeklyReadings.filter(dayTemperature => {
  return dayTemperature < 20;
 });
 if(colderDays) {
  console.log("Ano, byly chladnější dny minulý týden");
 } else {
  console.log("Ne, nebyly žádné chladnější dny");
 }
 

Všimli jste si něčeho? Pokud ano, skvělá práce! Podmínka `if` na konci kontroluje `colderDays`, což je ve skutečnosti pole! Mnoho lidí tuto chybu dělá. Problém s touto podmínkou je, že JavaScript je v mnoha ohledech podivný a nekonzistentní jazyk a „pravdivost“ věcí je jednou z nich. I když `[] == true` vrací false, uvnitř podmínky `if`, `[]` se vyhodnotí jako pravda! Jinými slovy, kód, který jsme napsali, nikdy neřekne, že minulý týden nebyly žádné chladnější dny.

Oprava je jednoduchá, jak je uvedeno v předchozím kódu. Kontrolujeme `colderDays.length`, což nám zaručí celé číslo (nulu nebo vyšší) a funguje tak v logických porovnáních. `filter()` vždy vrací pole, ať už prázdné nebo neprázdné, takže se na to můžeme spolehnout a s jistotou psát naše logické srovnání.

Byla to delší odbočka, než jsem plánoval, ale chyby jako je tato, stojí za to zdůraznit, abyste se vyhnuli mnoha hodinám ladění! 🙂

Funkce reduce()

Ze všech funkcí v tomto článku patří `reduce()` k těm „matoucím a divným“. I když je tato funkce důležitá a vede v mnoha situacích k elegantnímu kódu, většina vývojářů JavaScriptu se jí vyhýbá a raději píše detailnější kód.

Důvod je ten, že `reduce()` je obtížné pochopit jak konceptuálně, tak z hlediska implementace. Když si přečtete její popis, přečtete si ho několikrát a stále pochybujete, zda jste ho nečetli špatně; a když to uvidíte v akci, váš mozek se zkroutí do tisíce uzlů! 🤭

Ale nebojte se. Funkce `reduce()` není ani zdaleka tak složitá, jako například B+ stromy a jejich algoritmy. Jde jen o to, že s tímto typem logiky se průměrný programátor během své práce jen zřídka setká.

Takže, po tom, co jsem vás vyděsil, a pak vám okamžitě řekl, abyste se nebáli, bych vám konečně rád ukázal, co tato funkce dělá a proč ji můžeme potřebovat.

Jak název napovídá, `reduce()` se používá ke zredukování něčeho. Věc, kterou redukuje, je pole, a věc, na kterou dané pole redukuje, je jediná hodnota (číslo, řetězec, funkce, objekt, cokoliv). Jinými slovy, `reduce()` transformuje pole na jedinou hodnotu. Všimněte si, že vrácená hodnota z `reduce()` není pole, jak je tomu u `map()` a `filter()`. Pochopit toto je už půlka úspěchu. 🙂

Pokud chceme transformovat (redukovat) pole, musíme dodat potřebnou logiku pomocí funkce. Této funkci říkáme redukční funkce a je prvním argumentem pro `reduce()`. Druhým argumentem je počáteční hodnota (číslo, řetězec, atd.), ke které se za chvíli dostaneme.

Zatím můžeme říct, že volání funkce `reduce()` vypadá takto: `array.reduce(reducerFunction, beginValue)`. Nyní se zaměřme na redukční funkci. Jak již bylo řečeno, funkce redukce říká `reduce()`, jak převést pole na jednu hodnotu. Vyžaduje dva argumenty: proměnnou, která funguje jako akumulátor (vysvětlíme si) a proměnnou pro aktuální hodnotu.

Vím, je to spousta terminologie pro jednu funkci, která není v JavaScriptu povinná. 😝😝 A to je důvod, proč se lidé `reduce()` vyhýbají. Ale pokud se to naučíte krok za krokem, nejenže to pochopíte, ale také to oceníte a stanete se lepším vývojářem.

Dobře, zpět k tématu. „Počáteční hodnota“ předávaná do `reduce()` je počáteční hodnota pro výpočet. Například, pokud budete v redukční funkci násobit, počáteční hodnota 1 dává smysl; nebo můžeme začít s 0 a tak dále.

Nyní se podíváme na signaturu pro funkci redukce. Funkce redukce předávaná do funkce `reduce()` má následující tvar: `reducerFunction(accumulator, currentValue)`. „Akumulátor“ je název pro proměnnou, která shromažďuje a uchovává výsledek výpočtu. Je to jako použití proměnné s názvem `total` k sečtení všech prvků v poli pomocí něčeho jako `total += arr[i]`. Přesně takto se funkce redukce používá v `reduce()`: akumulátor je na začátku nastaven na počáteční hodnotu, kterou zadáte, a pak se jeden po druhém navštíví prvky v poli, provede se výpočet a výsledek se uloží do akumulátoru, a tak dále.

Co je tedy ta „aktuální hodnota“ v redukční funkci? Je to stejná myšlenka, jakou byste si představili, kdybych vás požádal o iteraci pole: vzali byste proměnnou, která by začínala na indexu nula, a posouvali ji o krok vpřed. Když to děláte, a já vás požádám, abyste se zastavili, ocitli byste se na jednom z prvků pole, že? To je to, co máme na mysli pod pojmem aktuální hodnota: je to hodnota proměnné použité k reprezentaci položky pole, která je aktuálně zvažována.

Nyní je čas podívat se na jednoduchý příklad a uvidět, jak se celý tento žargon spojuje v reálném volání `reduce()`. Řekněme, že máme pole obsahující prvních n přirozených čísel (1, 2, 3 … n) a zajímá nás nalezení faktoriálu n. Víme, že pro nalezení n! jednoduše potřebujeme vše vynásobit, což nás vede k této implementaci:

 const numbers = [1, 2, 3, 4, 5];
 const factorial = numbers.reduce((acc, item) => acc * item, 1);
 console.log(factorial); // 120
 

V těchto pouhých třech řádcích kódu se toho děje hodně, takže to pojďme rozebrat jeden po druhém. Jak je vidět, `numbers` je pole, které obsahuje všechna čísla, která chceme násobit. Dále se podívejte na volání `numbers.reduce()`, které říká, že počáteční hodnota pro `acc` by měla být 1 (protože neovlivňuje žádné násobení). Dále zkontrolujte tělo redukční funkce, `(acc, item) => acc * item`, která jednoduše říká, že návratová hodnota pro každou iteraci v poli by měla být tato položka vynásobená tím, co je již v akumulátoru. Iterace a ukládání násobení do akumulátoru je to, co se děje v pozadí, a je jedním z důvodů, proč je `reduce()` takový kámen úrazu pro vývojáře JavaScriptu.

Proč používat `reduce()`?

Je to dobrá otázka a nemám na ni jednoznačnou odpověď. Cokoliv `reduce()` dělá, lze provést pomocí cyklů, `forEach()` atd. Tyto techniky však vedou k mnohem většímu množství kódu, což ztěžuje čtení. Pak je tu starost o neměnnost: s funkcí `reduce()` si můžete být jisti, že vaše původní data nebyla změněna. To samo o sobě eliminuje velké množství chyb.

A konečně, `reduce()` je mnohem flexibilnější v tom, že akumulátorem může být objekt, pole nebo dokonce funkce. To samé platí pro počáteční hodnotu. Existuje velká flexibilita při návrhu opakovaně použitelného kódu.

Pokud stále nejste přesvědčeni, je to v pořádku. Komunita JavaScriptu je rozdělena na názory o „kompaktnosti“, „eleganci“ a „síle“ funkce `reduce()`, takže je v pořádku, pokud ji nepoužíváte. 🙂 Ale určitě se podívejte na nějaké příklady, než se rozhodnete `reduce()` nepoužívat.

Funkce some()

Mějme pole objektů, kde každý objekt představuje osobu. Chcete vědět, zda jsou v poli lidé starší 35 let. Nemusíte počítat, kolik jich je, ani získávat jejich seznam. Stačí vám zjistit, zda alespoň jeden takový člověk v poli je.

Jak to uděláte?

Můžete vytvořit proměnnou a procházet pole pomocí cyklu:

 const persons = [
  {
   name: 'Osoba 1',
   age: 32
  },
  {
   name: 'Osoba 2',
   age: 40
  },
 ];
 let foundOver35 = false;
 for (let i = 0; i < persons.length; i ++) {
  if(persons[i].age > 35) {
   foundOver35 = true;
   break;
  }
 }
 if(foundOver35) {
  console.log("Ano, jsou tu nějací lidé!");
 }
 

Problém? Kód je příliš podobný C nebo Javě. „Verbose“ je další slovo, které mě napadá. Zkušený vývojář JS by mohl říct, že je „ošklivý“, „strašný“ atd. 😝 A právem, řekl bych. Jedním ze způsobů, jak tento kód vylepšit, je použít například `map()`, ale i tak je řešení trochu těžkopádné.

Naštěstí máme úhlednou funkci s názvem `some()`. Tato funkce pracuje s poli a přijímá vlastní „filtrovací“ funkci, která vrací booleovskou hodnotu true nebo false. Dělá to, o co jsme se posledních pár minut snažili, jen stručně a elegantně. Můžeme ji použít takto:

 const persons = [
  {
   name: 'Osoba 1',
   age: 32
  },
  {
   name: 'Osoba 2',
   age: 40
  },
 ];
 if(persons.some(person => {
  return person.age > 35
 })) {
  console.log("Nějací lidé nalezeni!");
 }
 

Stejný vstup, stejný výstup, ale všimněte si masivního zkrácení kódu! Všimněte si také, jak se snižuje kognitivní zátěž, protože již nepotřebujeme analyzovat kód řádek po řádku, jako bychom byli sami interpretem! Kód se nyní čte téměř jako přirozený jazyk.

Funkce every()

Stejně jako `some()` máme další užitečnou funkci s názvem `every()`. Jak už jste asi uhodli, i tato vrací booleovskou hodnotu v závislosti na tom, zda všechny prvky v poli splňují daný test. Test, který je třeba splnit, je obvykle dodáván jako anonymní funkce. Ušetřím vás popisování naivní verze kódu, takže zde je ukázka použití `every()`:

 const entries = [
  {
   id: 1
  },
  {
   id: 2
  },
  {
   id: 3
  },
 ];
 if(entries.every(entry => {
  return Number.isInteger(entry.id) && entry.id > 0;
 })) {
  console.log("Všechny záznamy mají platné ID")
 }
 

Jak je vidět, kód kontroluje všechny objekty v poli, zda mají platnou vlastnost `id`. Definice „platného“ závisí na kontextu problému, ale v tomto kódu jsem zvažoval nezáporná celá čísla. Opět vidíme, jak jednoduché a elegantní je čtení kódu.

Funkce includes()

Jak zkontrolujete existenci podřetězců a prvků pole? Pokud jste jako já, sáhnete po `indexOf()` a poté vyhledáte dokumentaci, abyste zjistili možné návratové hodnoty. Je to zbytečné a návratové hodnoty se těžko pamatují.

Existuje však lepší alternativa: `includes()`. Použití je stejně jednoduché jako název a výsledný kód je příjemný. Pamatujte, že porovnávání u `includes()` rozlišuje velká a malá písmena, což je intuitivní. A teď je čas na kód!

 const numbers = [1, 2, 3, 4, 5];
 console.log(numbers.includes(4));
 const name = "Ankush";
 console.log(name.includes('ank')); // false, protože první písmeno je malé
 console.log(name.includes('Ank')); // true, jak se očekávalo
 

Od této metody však nečekejte příliš mnoho:

 const user = {a: 10, b: 20};
 console.log(user.includes('a')); // selže, protože objekty nemají metodu "includes"
 

Nemůže se dívat dovnitř objektů, protože pro objekty není definována. Ale víme, že funguje na polích, takže možná můžeme něco vymyslet… 🤔.

 const persons = [{name: 'Phil'}, {name: 'Jane'}];
 persons.includes({name: 'Phil'});
 

Co se stane, když spustíte tento kód? Neselže, ale výstup je falešný. 😫😫 Je to způsobeno objekty, ukazateli a tím, jak JavaScript vidí a spravuje paměť. Pokud se chcete ponořit hlouběji, můžete, ale zde se zastavím.

Můžeme ale dosáhnout toho, aby se kód choval správně, pokud ho přepíšeme takto, ale v tomto bodě to přestává dávat smysl:

 const phil = {name: 'Phil'};
 const persons = [phil, {name: 'Jane'}];
 persons.includes(phil); // true
 

Přesto to ukazuje, že můžeme, aby `includes()` fungovalo na objektech. 😄

Funkce slice()

Řekněme, že máte řetězec a já vás požádám, abyste vrátili jeho část, která začíná písmenem „r“ a končí „z“ (skutečná písmena nejsou důležitá). Jak byste k tomu přistoupili? Možná byste vytvořili nový řetězec a uložili do něj potřebné znaky a vrátili ho. Nebo, pokud jste jako většina programátorů, dali byste mi dva indexy pole: jeden pro začátek a druhý pro konec podřetězce.

Oba tyto přístupy jsou v pořádku, ale existuje koncept nazývaný krájení, který nabízí elegantní řešení. Naštěstí neexistuje žádná komplikovaná teorie, krájení znamená přesně to, co zní – vytvoření menšího řetězce/pole z daného, podobně jako krájení ovoce. Podívejme se na příklad:

 const headline = "A dnes večer, host, na kterého jsme všichni čekali!";
 const startIndex = headline.indexOf('host');
 const endIndex = headline.indexOf('čekali');
 const newHeadline = headline.slice(startIndex, endIndex);
 console.log(newHeadline); // host, na kterého jsme všichni
 

Když voláme `slice()`, dáváme JavaScriptu dva indexy – jeden, kde chceme krájení začít, a druhý, kde chceme, aby skončilo. Koncový index není zahrnut v konečném výsledku, proto v novém nadpisu chybí slovo „čekali“.

Krájení je běžné v jiných jazycích, zejména v Pythonu. Tito vývojáři by řekli, že si bez této funkce nedokážou představit život.

Krájení je elegantní a pohodlné a není důvod ho nepoužívat. Není to syntaktický cukr s penaltou za výkon, protože vytváří mělké kopie původního pole/řetězce. Vývojářům JavaScriptu doporučuji, aby se s `slice()` seznámili a přidali si ho do svého arzenálu!

Funkce splice()

Metoda `splice()` zní jako příbuzná metody `slice()` a v některých ohledech můžeme tvrdit, že tomu tak je. Obě vytvářejí nová pole/řetězce z původních, s jedním malým, ale důležitým rozdílem – `splice()` odstraňuje, mění nebo přidává prvky, ale upravuje původní pole. Toto „zničí“ původní pole a může způsobit velké problémy, pokud nejste opatrní nebo nerozumíte hlubokým kopiím a referencím. Zajímalo by mě, proč vývojáři nepoužili stejný přístup jako u `slice()` a ponechali původní pole beze změny, ale můžeme být k jazyku, který byl vytvořen za pouhých deset dní shovívaví.

Podívejme se, jak `splice()` funguje. Ukážu příklad, kde odstraníme několik prvků z pole, protože to je nejčastější použití, které pro tuto metodu najdete. Také se zdržím uvádění příkladů sčítání a vkládání, protože ty se dají snadno vyhledat.

 const items = ['vejce', 'mléko', 'sýr', 'chléb', 'máslo'];
 items.splice(2, 1);
 console.log(items); // [ 'vejce', 'mléko', 'chléb', 'máslo' ]
 

Výše uvedené volání `splice()` říká: začněte na indexu 2 (tedy na třetím místě) pole a odeberte jednu položku. V daném poli je „sýr“ třetí položkou, takže se odstraní a pole se zkrátí. Mimochodem, odstraněné položky se vrací pomocí `splice()` ve formě pole, takže pokud bychom chtěli, mohli jsme „sýr“ uložit do proměnné.

Podle mých zkušeností, mají `indexOf()` a `splice()` velkou synergii. Najdeme index položky a pak ji z daného pole odstraníme. Není to vždy nejefektivnější metoda a často je použití klíčů objektu (ekvivalent hash mapy) mnohem rychlejší.

Funkce shift()

`shift()` je pohodlná metoda pro odstranění prvního prvku pole. To samé můžeme udělat pomocí `splice()`, ale `shift()` je snadněji zapamatovatelná a intuitivní, když chcete odstranit první prvek.

 const items = ['vejce', 'mléko', 'sýr', 'chléb', 'máslo'];
 items.shift()
 console.log(items); // [ 'mléko', 'sýr', 'chléb', 'máslo' ]
 

Funkce unshift()

Stejně jako `shift()` odstraňuje první prvek z pole, `unshift()` přidává nový prvek na začátek pole. Použití je stejně jednoduché:

 const items = ['vejce', 'mléko'];
 items.unshift('chléb')
 console.log(items); // [ 'chléb', 'vejce', 'mléko' ]
 

Na rozdíl od metod `push()` a `pop()` jsou `shift()` a `unshift()` extrémně neefektivní. Pokud pracujete s velkými poli (řekněme 2000+ prvků), příliš mnoho volání těchto funkcí může vaši aplikaci zpomalit.

Funkce fill()

Někdy potřebujete změnit několik položek na jednu hodnotu, nebo dokonce „resetovat“ celé pole. V těchto situacích vás `fill()` ušetří od cyklů a chyb typu off-by-one. Můžeme ji použít k nahrazení části nebo celého pole danou hodnotou. Podívejme se na příklady:

 const heights = [1, 2, 4, 5, 6, 7, 1, 1];
 heights.fill(0);
 console.log(heights); // [0, 0, 0, 0, 0, 0, 0, 0]
 const heights2 = [1, 2, 4, 5, 6, 7, 1, 1];
 heights2.fill(0, 4);
 console.log(heights2); // [1, 2, 4, 5, 0, 0, 0, 0]
 

Další funkce

I když výše uvedený seznam pokrývá funkce, které většina vývojářů JavaScriptu používá, není v žádném případě úplný. V JavaScriptu je mnoho menších, ale užitečných funkcí (metod). Zde je pár dalších, které stojí za zmínku:

  • reverse()
  • sort()
  • entries()
  • fill()
  • find()
  • flat()

Doporučuji vám se na ně alespoň podívat.

Závěr

JavaScript je velký jazyk, navzdory malému počtu základních konceptů, které je třeba se naučit. Mnoho funkcí (metod), které máme k dispozici, tvoří většinu jeho velikosti. Protože je však JavaScript pro většinu vývojářů sekundární jazyk, neponoříme se dostatečně hluboko a přicházíme o mnoho krásných a užitečných funkcí, které nabízí. Stejné platí pro koncepty funkcionálního programování. 😅

Věnujte čas zkoumání jazyka (a knihoven jako Lodash). I pár minut strávených tímto úsilím povede ke zvýšení produktivity a mnohem čistšímu a kompaktnějšímu kódu.