Přejít k navigační liště

Zdroják » JavaScript » Custom Elements v praxi

Custom Elements v praxi

Články JavaScript

Jak se něco dozvědět o nové technologii? Vyzkoušet ji v praxi! Mám za sebou už pár neseriózních pokusů s vlastními HTML značkami a v minulých dnech a týdnech jsem s jejich pomocí přepsal regulérní aplikaci webového klienta pro MPD, nazvaného CYP. Co jsem se při tom naučil a dozvěděl?

Nálepky:

Slaďme očekávání

Tento článek popisuje zkušenosti s použitím technologie Custom Elements. Jedná se o podmnožinu většího balíku Web Components, který kromě vlastních značek přináší ještě HTML Templates a především velmi komplexní Shadow DOM. Pro potřeby zmíněného projektu bylo postačující využít pouze Custom Elements.

Zároveň článek nenabízí kvalitativní porovnání s alternativami (plain HTML, React, Vue, Svelte, …), jelikož autor nedisponuje adekvátními vědomostmi o těchto nástrojích. Je dobré vědět, že Custom Elements (a Web Components obecně) rozhodně nejsou všespásnou technologií a pro různé potřeby je nutné volit různá řešení. Následující text je proto jen souhrnem zážitků z implementace Custom Elements, názor si čtenář bude muset udělat sám. To ovšem jistě nebude problém.

Co jsou Custom Elements

Podstata je překvapivě jednoduchá. Jedná se o standardizovaný způsob, jak v HTML vytvořit a používat další vlastní značky. Pokud nám tedy oficiální množina značek (<h1>, <p>, <header>, <a>, ...) nestačí a náš kód se hemží samými divy, je to prostor pro zvážení zavedení značek vlastních.

Nejdůležitějším úkolem HTML značek je obohacení textu o strukturu a význam. Tato role je u vlastních značek diskutabilní, protože prohlížeč jejich význam nemůže chápat. Vlastní značky proto volíme v případě, že od nich očekáváme zejména specifické chování, případně vzhled.

Začneme tak trochu od konce, tím, jak své vlastní značky použijeme. Sestavíme z nich strom stránky a můžeme je libovolně kombinovat s normálními značkami. Řešený projekt je hudební přehrávač CYP, jeho HTML proto může vypadat takto:

<body>
    <cyp-app theme="dark" color="dodgerblue">
        <header>
            <cyp-player>
                <h1>Přehrávaná píseň</h1>
                <x-range></x-range>
                <button class="prev">⏮</button>
                <button class="play">▶️</button>
                <button class="pause">⏸</button>
                <button class="next">⏭</button>
            </cyp-player>
        </header>
        <main>
            <cyp-queue></cyp-queue>
            <cyp-playlists></cyp-playlists>
            <cyp-library></cyp-library>
        </main>
        <footer>
            <cyp-menu>
                <button data-for="queue">Queue</button>
                <button data-for="playlists">Playlists</button>
                <button data-for="library">Library</button>
            </cyp-menu>
        </footer>
    </cyp-app>
</body>

Za zmínku stojí, že vlastní značky musí mít v názvu pomlčku. To je garance dopředné kompatibility, neboť ve standardu HTML se značky s pomlčkami neobjevují a objevovat nebudou. Nestane se tedy, že by naše vlastní značka v budoucnu kolidovala s nějakou oficiální. Stále ovšem zůstává riziko jmenné kolize mezi značkami více různých vývojářů.

V ukázce jsou použity tyto vlastní značky:

  • <cyp-app> jako zastřešující obal nad celou aplikací;
  • <cyp-player> zobrazující právě přehrávanou píseň a nabízející interakci;
  • <cyp-queue>, <cyp-playlists> a <cyp-library> představují různé pohledy na písničky, playlisty a soubory;
  • <cyp-menu> je navigační prvek v patičce aplikace;
  • <x-range> je vstupní prvek na zobrazení průběhu přehrávání písně.

Vytvořit, nebo upravit?

Možná vás napadá, že pro některé použité vlastní značky již HTML nabízí docela vhodnou značku standardní. Kupříkladu navigace mezi komponentami aplikace by šla popsat pomocí <nav>. Nebo by se pro průběh přehrávané písničky dala upravit značka <input type=range>. To je dobrá chvíle vysvětlit, že Custom Elements mohou být dvojího typu:

  1. Samostatné (autonomous), které nemají žádnou vestavěnou funkcionalitu a čeká se, že jejich chování garantuje autor;
  2. Upravené (customized), které vycházejí (v terminologii OOP dědí) z již existující HTML značky a funkcí ji tedy mohou zastoupit.

Logické by proto bylo, aby <cyp-menu> vycházelo z HTML značky <nav>. Bohužel, podpora Customized Custom Elements je v prohlížečích zatím stále neúplná (nefungují v mobilním ani desktopovém Safari), takže jistější je zatím implementovat vlastní značky jako Autonomous Custom Elements.

Konečně JavaScript

Prohlížeč se k vlastním značkám staví indiferentně. Nevadí mu, nezpůsobují žádné chyby či problémy, ale zároveň není zřejmé, co by s nimi měl dělat. Je proto na autorovi, aby prohlížeči vysvětlil, co a jak.

Tomuto vysvětlení se formálně říká definování vlastní značky a dělá se to voláním JS funkce customElements.define(). V ní autor provede párování názvu značky (řetězec) a JS třídy, která obsahuje související funkcionalitu. Klíčové a logické přitom je, že zmiňovaná třída musí dědit z vestavěné třídy HTMLElement (která jí dodá očekávanou funkcionalitu, jako např. appendChild, innerHTML a podobně).

Téměř nejjednodušší příklad vlastní značky by tedy byl:

class MyElement extends HTMLElement {
    constructor() {
        super();
        alert("vlastni znacka");
    }
}

customElements.define("my-element", MyElement);

Konstruktor není povinný, ale dobře ilustruje, co se po definování stane: prohlížeč projde všechny již zpracované výskyty značky <my-element> a jeden po druhém je převede na objekty MyElement, tj. vytvoří jim nové instance (a vykoná jejich konstruktory). Pokud prohlížeč v budoucnu (do opuštění stránky) narazí na další značku <my-element>, rovnou vytvoří příslučnou instanci MyElement.

Za zmínku stojí, že proces definování můžeme provést nezávisle na tom, kdy se v HTML dokumentu vlastní značky vyskytnou. Pokud ale přidáme vlastní značce nějakou funkcionalitu (třeba novou metodu), nemůžeme ji zavolat, dokud nebude značka definována.

V JavaScriptovém kódu nyní můžeme vytvářet nové HTML značky buď tradičně pomocí document.createElement("my-element"), nebo prostou instancializací new MyElement(). Obojí vrátí stejný HTML prvek, v druhé variantě můžeme konstruktoru předat nějaké parametry.

Jak být slušným spoluobčanem

Vestavěné HTML prvky dodržují jistá pravidla, předepisovaná standardem DOM. Můžeme z nich stavět strom dokumentu, přistupovat ke společným vlastnostem a měnit jejich atributy. Custom Elements dovolují vývojářům toto chování pozorovat a reagovat na něj pomocí tzv. lifecycle callbacks. Jedná se o sadu metod, kterými ve vlastním prvku sledujeme jeho interakci se zbytkem stránky:

class MyElement extends HTMLElement {
    connectedCallback() {
        alert("tento prvek byl připnut do jiného prvku ve stránce");
    }

    disconnectedCallback() {
        alert("tento prvek již není součástí stránky");
    }

    adoptedCallback() {
        alert("tento prvek byl přesunut do jiného dokumentu");
    }
}

Poslední a patrně nejužitečnější lifecycle callback se týká práce s atributy. Vlastní prvek z předka HTMLElement dědí metody setAttribute a další; jejich volání lze sledovat. Musíme ale říci, které změny (resp. změny kterých atributů) nás zajímají:

class MyElement extends HTMLElement {
    static get observedAttributes() {
        return ["a", "b"];
    }

    attributeChangedCallback(name, oldValue, newValue) {
        console.log("Atribut", name, "změněn z", oldValue, "na", newValue);
    }
}

customElements.define("my-element", MyElement);
let me = new MyElement();
me.setAttribute("a", "test"); // console.log
me.setAttribute("x", "y");    // atribut se nastavi, ale callback neprobehne

Práce s atributy není vždy ideální. Mohou to být jen řetězce a u běžných HTML prvků jsme zvyklí, že jejich funkcionalita bývá dostupná také pomocí JS reflektovaných vlastností. Proto můžeme (i když to není povinné) atributy do vlastností zrcadlit:

class MyElement extends HTMLElement {
    static get observedAttributes() {
        return ["a"];
    }

    attributeChangedCallback(name, oldValue, newValue) {
        console.log("Atribut", name, "změněn z", oldValue, "na", newValue);
    }

    get a() { return this.getAttribute("a"); }
    set a(a) { this.setAttribute("a", a); }
}

customElements.define("my-element", MyElement);
let me = new MyElement();
me.a = "test"; // console.log

Styly a Shadow DOM

Jak vzniklé komponenty stylovat? Zcela tuctově pomocí CSS, kde v selektorovém jazyce můžeme používat názvy vlastních značek. Pro navigační patičku pak třeba:

cyp-menu button:not(:disabled) {
    cursor: pointer;
}

Komponentové systémy často dovolují defenzivní přístup, kdy jednotlivé části stránky neovlivňují své okolí a zároveň jsou uzamčeny vůči nežádoucím nahodilým změnám zvenčí. To koliduje s globální náturou jazyka CSS. Řešení tohoto problému se nazývá Shadow DOM a jedná se o technologii, která dovede podstrom dokumentu zapouzdřit tak, aby se do něj nepromítaly vnější styly (dokonce tento podstrom není následně přístupný ani JavaScriptovým metodám pro procházení dokumentu). Práce s Shadow DOM je ovšem velmi komplexní a jeho podpora v prohlížečích je opět neúplná. Protože v projektu přehrávače CYP nehrozí interference komponent různých autorů, tato technika není nutná.

Hierarchie

Komponenty aplikace CYP komunikují pomocí architektury client-server se vzdáleným hudebním přehrávačem MPD. K tomu využívají technologie Web Sockets. Je zde otázka, jakým způsobem má či může JS kód jednotlivých komponent k tomuto zdroji přistupovat.

Různých řešení je mnoho: singleton, depedency injection, service locator… pro hierarchii Custom Elements mi přišlo přímočaré, aby majitelem instance WebSocket byla zastřešující značka <cyp-app>. Její komponenty se k socketu dostanou tak, že v rámci hierarchie svých rodičů naleznou tuto značku:

class CypApp extends HTMLElement {
    constructor() {
        this.socket = new WebSocket("...");
    }
}

class CypComponent extends HTMLElement {
    get socket() {
        return this.closest("cyp-app").socket;
    }
}

Tento kód bude ovšem fungovat jen za předpokladu, že značka <cyp-app> již byla definována. Pokud takovou garanci v komponentě potomka nemáme, můžeme využít Promise vrácenou metodou customElements.whenDefined():

async function test() {
    await customElements.whenDefined("cyp-app");
    console.log(document.querySelector("cyp-app").socket);
}

Závěrem

Byl jsem vyzván, abych se ve světle nabytých zkušeností vyjádřil k článku Richarda Harrise (mj. autora nástrojů Rollup a Svelte), ve kterém kritizuje Web Components. Nuže, ve zkratce:

  1. Aplikace CYP stojí a padá na WebSocketové komunikaci s MPD. Otázku fungování bez JS není třeba řešit.
  2. Shadow DOM jsem nepoužil.
  3. ¯\_(ツ)_/¯
  4. Žádný polyfill jsem nepoužil. Cílím na množinu prohlížečů, které Custom Elements V1 podporují.
  5. Výhrada se týká slotted elements z HTML Templates, nepoužil jsem.
  6. Ano, dualita vlastností a atributů je problematická. Vnímám ji jako daň za kompatibilitu s HTML.
  7. To mi vrásky nedělá.
  8. Ukázka by zabrala polovinu místa, kdyby autor reflexi vlastností naimplementoval cyklem defineProperty na Adder.prototype.
  9. V aplikaci CYP s vlastními komponentami jsem na tento problém nenarazil.
  10. https://www.youtube.com/watch?v=KauRmlffjqc

Komentáře

Subscribe
Upozornit na
guest
7 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
Josef Marianek

Zase jsem se neco noveho dozvedel. Posledni dobou jen koukam, co vsechno je v html5 a modernim js mozne. Popravde, uz jsme prepsali jednu puvodne c++/qt aplikaci do web verze a sice neumi uplne vsechno, ale skoro vsechno. Tyhle custom html elementy se urcite budou hodit.

Vít Heřman

Mám velmi rád všechny vaše články a talky o JS. Vaším článkem z roku „JavaScriptové MVC frameworky“ z roku 2013 jsem se inspiroval velmi dlouho až do příchodu Reactu. Článek by zasloužil rozšíření o React, Svelte, Vue, možná nový Angular a další. Zajímal by mne Váš názor, zda kritika alespoň někde trochu nepolevila :-) Byť vím, že máte mnohem raději low level technologie, a že v žádném frameworku asi nikdy nic skutečně dělat nebudete. Děkuji :-)

Mlocik97

Svelte/Sapper > Kdejaký JS Framework (ako VueJS, Angular, či React (ktorý je frameworkom, když sa rozšíri o 12 ďalších knižníc/rozšírení/modulov či jak to chce nazvať)) React ale pravda že samo o sobe je len knižnica, navyše je nabobtnaná a extrémne nezmyselná. Nie len že aby to fakt fungovalo je potreby to mať spolu s 12 knižnicami, čož je proste moc veľký objem, ktorý lejeme do štamprlíka, ale ešte má toľko chaotických a nezmyselných vlastností. Ceľkovo to perfektne zhrnuté je https://www.reddit.com/r/reactjs/comments/bv9nxf/react_sucks/

najhorší je podla mňa boilerplate. Vo Svelte viem z externého Rest API získať dáta v JSON podobe a vyparsovať je do tabuľky v 5 riadkoch kódu, v Reacte je to minimálne 40 riadkov.

skuste v Reacte na koľko riadkov by ste spravili jednoduchú kalkulačku na sčitovanie 2 čísel, čo vo Svelte spravíte za pomoci 1 riadka JS a 3 riadkov HTML a to:

let a = 0, b = 0;


<input type="number" bind:value={a}>
<input type="number" bind:value={b}>
<p>{a} + {b} = {a + b}</p>

skuste si toto v Reacte,…

taká jednoduchá vec, v Reacte na 20 riadkov JS. Ako menej boilerplatu som ešte nevidel (vo Svelte), snaď len v AngularJS, kde ste nemuseli deklarovať premenné, tzv, interaktívne sčitovanie 2 čísiel na 0 riadkov JS, a 3 riadky HTML. To v čistom Reacte neurobíte ani keby ste sa postavili na hlavu. Ako Svelte/Sapper je momentálne najlepšie riešenie a extrémne jednoduchá vec pre aj úplných začiatočníkov. Pomocou Svelte/Sapper by aj totálny laik, človek čo sotva vie zapnúť prehliadač by dokázal naprogramovať plnohodnotnú kalkulačku, plnehodnotný informačný web o covid-19 (ako je napr. worldometers) alebo kludne aj vlastný blog (bez možnosti komentovať články, či prihlasovania), a to za jeden deň úplne v pohode. V Reacte, ja čo už mám za sebou aj väčšie projekty, mám v Reacte problém naprogramovať najtriviálnejšie veci, zatiaľ čo vo Svelte/Sapper či AngularJS dokážem naprogramovať komplexné web aplikácie či desktop aplikácie (v Electron či Svelte-native)

Osobne tvrdím že React by mal umrieť, pretože tá vec nie je k ničomu dobrá, nemá ani jednu výhodu voči Svelte/Sapper. No možno jednu, že je preň už veľa hotových modulov/component či jak to nazvete.

Btw. toto je len môj názor, nemusíte súhlasiť ak nechcete, ale i tak ak ste React favorit, tak odporúčam vyskúšať Svelte/Sapper a verte mi že vätšina z Vás už ho nebudete nikdy chcieť ani vidieť, a zostanete pri Svelte/Sapper.

L.

React nabobtnalý? „Extrémně nesmyslný“? Tak to asi používáme každý nějaký jiný React. Ten ve kterém píšu já je geniálně jednoduchý a velmi logický (pokud se člověk zbaví zlozvyků, neprasí a dělá věci jak se dělat mají). Nějaké počty knihoven jsou nesmyslné měřítko, záleží na tom, jakou kdo zvolí granularitu.

Získání JSON z RESTu a vypsání do tabulky? Komunikace přes REST je jaksi naprosto mimo React, nicméně přes třeba modernější graphql by to s knihovní komponentou mohlo vypadat nějak takhle:

() => (<Query query={QUERY}>
  ({data}) => {data && <table>
   {data.map(row => <tr key={row.id}><td>{row.id}</td><td>{row.nazev}</td></tr>)}
  </table>})
</Query>)

Dalo by se to zformátovat i přehledněji, ale pak by někteří mohli namítat, že je to na moc řádek :) A samozřejmě kdybych použil nějakou knihovnu na tabulky, tak je to ještě jednodušší.

A že je tam moc bolerplate? Kde proboha? React je naopak extrémně úsporný, protože komponenta je prostě funkce. Třeba ta sčítačka

() => {
  const [a, setA] = useState(0)
  const [b, setB] = useState(0)

  return (<>
    <input type="number" value={a} onChange={(event) => setA(event.value)} />
    <input type="number" value={b} onChange={(event) => setB(event.value)} />
    {a} + {b} = {a+b}
  </>)
}

Je to devět řádků a samozřejmě by se to dalo zformátovat na méně :) (Sorry, jestli v tom handlingu eventů mám někde blbě název, na tohle se v reálu samozřejmě používá knihovna.)

Obecně porovnávat knihovny / frameworky na takovýchhle trivialitách je nesmysl. Když má příklad pět řádků, tak pak to, že se React komponenta píše jako funkce přidá opticky 40% řádků, ale reálně je to naprosto marginální. Použitelnost knihovny/frameworku se opravdu projeví až na nějakém větším projektu. Malý projekt se vždycky nějak naprasí.

Na Svelte jsem se koukal a nijak mě neuchvátil. Spíš naopak, přijde mi to jako taková splácanina. Třeba speciální tagy na if/then/else / cykly. Tenhle přístup se neosvědčil už u JSP. Jenže mezitím vyrostla nová generace, která to už nepamatuje a tak si potřebuje nabít nos znovu :-)

No a když jsem se kouknul na vygenerovaný JS, tak to byl teda děs běs. V tom bych nechtěl něco ladit. Na Reactu je geniální to, že komponenty jsou prostě JS/TS kód, maximálně s JSX kompilací. Navíc, u trochu větší aplikace přesáhne tenhle vygenerovaný bolerplate podle mě velikost knihovny/knihoven Reactu, takže balíček aplikace bude větší.

Což mě vede k pár otázkám ohledně Svelte:

  • Funguje s ním napovídání v IDE (možné atributy u custom komponent)?
  • Funguje tam typová kontrola (když komponenta chce property nějakého typu, tak tam nejde dát jiný typ, samozřejmě předpokládám TS)?
  • Pro Redux jsou výborné Redux DevTools, kde vidím obsah store, akce, mohu si tím procházet v čase zpět a zase dopředu, na ladění naprosto geniální (to je nevýhoda lokálních stavů, které jsou sice jednodušší na použití, ale hůře se ladí). Existuje podobný debugger pro Svelte?
  • Pro React existují React Developer Tools, kde mohu vidět strom komponent, jejich properties atp. Existuje něco podobného pro Svelte?
  • Musím kvůli každé komponentě zakládat nový soubor (v Reactu ne, ve Svelte, zdá se, ano)?

A na konec jedna složitější otázka. Za naprosto geniální vlastnost hooků považuji to, že je možné s nimi obalit nějakou business logiku, která mi vystaví nějaké proměnné a funkce na manipulaci s nimi. Tuhle logiku pak mohu jednoduše napojovat do různých komponent. O něco podobného jsem se snažil v různých jiných frameworcích, ale až v Reactu je to opravdu jednoduché a elegantní:

const useCounter = (initial?: number = 0) => {
  const [count, setCount] = useState(initial)

  const increment = () => setCount(Math.min(count+1, 10))
  const decrement = () => setCount(Math.min(count-1, 0))
  return [ count, increment, decrement ]
}

() => {
  const [ count, increment, decrement ] = useCounter()

  return (<>
    Count: {count}
    <button onClick={decrement}>-</button>
    <button onClick={increment}>+</button>
  </>)
}

Tady funkce/hook useCounter obaluje logiku čítače od jedné do desíti. Tohle je samozřejmě jednoduchý příklad, v reálu by ta logika byla složitější. Použití v komponentách je pak triviální jeden řádek – volání funkce. Žádný kód okolo. Lze něco podobně elegantního udělat ve Svelte?

Abych to shrnul: Za svůj docela dlouhý programátorský život jsem dělal v mnoha frameworcích. A React je z nich jednoznačně nejjednodušší a nejelegantnější. To platí i pro jeho kamaráda Redux, spolu tvoří opravdu silné kombo. Asi jediná věc, která občas dělá problémy je právě „stavebnicovost“ toho systému. Je spousta možností, jak k tvorbě aplikace přistoupit, takže je potřeba se v týmu sjednotit na jednom přístupu, aby aplikace nebyla „každý pes jiná ves“. A když různí členové mají různé pozadí a různé oblíbené přístupy, tak to ze začátku může trochu drhnout.

Mlocik97

K tvojim argumentom sa radši nejdu vyjadrovať, lebo vidieť že zo Svelte/Sapper si videl nula celá nič riadkov kódu. Jediný argument, s ktorým u teba môžem súhlasiť je podpora typovej kontroly (TS), ale i na tom sa pracuje. Všetko ostatné čo si napísal sú nezmysli. Aj ten boilerplate ešte si napísal viac riadkov a ešte to obhajovať jak je dobrý React s tým že s tým že mu pripisuješ vlastnosť úplne inej knižnice. Toľko nezmyslov čo si tu napísal ani nehovorím. React != Redux != GraphQL.

Btw, iba si potvrdil minimálne môj argument (ktorý je rovnaký ako v odkaze na reddit):

need to install 12 different packages to make it useful
React is just Angular if you hacked 12 libraries together that aren’t meant to work together cohesively.

L.

Ale to nebyly argumenty, to byly otázky :-) Nicméně tedy beru jako odpověď, že Svelte nemá pořádnou typovou kontrolu, má mizernou (až žádnou?) podporu v IDE, ladění generovaného kódu je obtížné a prakticky neexistují specializované vývojářské nástroje.

Aj ten boilerplate ešte si napísal viac riadkov

Ty jsi tvrdil, že sčítačka v Reactu bude mít 20 řádků. Moje má 9. Co jsem se posledně koukal, tak 9 je méně, než 20 ;-)

Nikde jsem netvrdil, že Redux / GraphQL (client) je React. Jsou to knihovny / součásti ekosystému, které se dají s Reactem použít, pokud je to pro daný případ potřeba. Zajímalo by mne, kde se bere ten nesmyslný přístup, že jedna knihovna musí nutně řešit všechny problémy. V programování jde o výsledek. Kolik „import“ mám ve zdrojáku je prakticky jedno.

Jinak, rozumně vyjadřovat se dá i k naprosto mimózním argumentům. Ukážu to na tom tvém (zprostředkovaném) „o dvanácti knihovnách“:

need to install 12 different packages to make it useful
React is just Angular if you hacked 12 libraries together that aren’t meant to work together cohesively.

  • Nevím, jak dotyčný došel zrovna k číslu 12, nikde neuvádí jejich seznam
  • Ze stejného důvodu lze těžko zjistit, kde přišel na to, že nejsou určené pro spolupráci s Reactem. Protože knihovny co používám já jako Redux, GraphQL client, Material UI, Redux Forms, Next.js, … pro spolupráci s Reactem určené přímo jsou a žádné hackování není potřeba (*)
  • Ani s těmi knihovnami není React Angular. React je založený na funkcionálních/třídních komponentách, mělkém porovnávání parametrů a virtuálním DOMu. Angular je založený na custom elementech. Úplně jiná technologie a styl psaní.
  • No a poslední a nejdůležitější argument: Až najdu zákazníka, který bude řešit, kolik knihoven v aplikaci importuji, tak se tím začnu zabývat. Zatím jsem takového nepotkal. Takže ten argument je naprosto mimo už jen kvůli tomuhle.

*) Pokud chceš vidět něco, co je opravdu „hacked together“, podívej se na JSF. Takový bastl jsem ještě neviděl. Děs běs.

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.

Pocta C64

Za prvopočátek své programátorské kariéry vděčím počítači Commodore 64. Tehdy jsem genialitu návrhu nemohl docenit. Dnes dokážu lehce nahlédnout pod pokličku. Chtěl bych se o to s vámi podělit a vzdát mu hold.