A JavaScript-ben a függvények alapvető szerepet játszanak a programozásban, hiszen lehetőséget adnak arra, hogy a kódot logikailag elkülönítsük és újra felhasználjuk. A függvények létrehozásának és használatának számos különböző módja létezik. A függvények lehetnek deklarációk, amelyek egy teljes kódsort alkotnak, vagy kifejezések, amelyek mindig más kódrészletekhez, például változóhoz vagy más függvényekhez kapcsolódnak. Ez a különbség meghatározza a kód szintaktikai felépítését és működését.
A függvénydeklarációk a következő alapvető szerkezetet követik: a function kulcsszó, ezt követi a kötelező függvénynév és egy zárójelek közötti paraméterlista, amelyek opcionálisan tartalmazhatnak paramétereket. A függvény törzse, amely lehet üres is, mindig egy nyitó és záró kapcsos zárójelek között van. Fontos megjegyezni, hogy a függvénydeklarációnak mindig saját állításnak kell lennie a JavaScript kódban, tehát nem lehet egyszerű kifejezés. Ezt a különbséget a következő példában láthatjuk:
Ebben a példában észrevehetjük, hogy egy függvényt másik függvényen belül definiálunk, ami JavaScript-ben teljesen normális. Az ilyen típusú elrendezés a JavaScript függvények fontosságát hangsúlyozza, és arra is utal, hogy a JavaScript rendkívül rugalmas, amikor a függvények szerepéről van szó.
A függvények másik típusa a függvénykifejezés. Mivel a JavaScript-ben a függvények elsőrendű objektumok, ugyanúgy lehet őket változókhoz rendelni, más függvények paramétereként használni, vagy más értékekkel visszaadni. A függvények tehát nemcsak az alapszintű deklarációkban, hanem kifejezésekként is szerepelhetnek. Például:
A fenti példában a myFunc egy függvénykifejezés, amelyet egy változóhoz rendelünk. Ez a kód szintaktikai értelemben nem egy függvénydeklaráció, hanem egy olyan kifejezés, amely a függvényt tartalmazza. A különbség abban rejlik, hogy míg a függvénydeklarációknál a függvénynév kötelező, a függvénykifejezéseknél a név nem szükséges. A kód olvashatósága érdekében azonban célszerű a függvényeknél minden esetben nevet adni.
A függvénykifejezések egyik előnye, hogy lehetővé teszik számunkra, hogy ott hozzunk létre függvényeket, ahol szükség van rájuk, így a kódunk tisztább és érthetőbb lesz. A következő példákban láthatjuk a különbséget a függvénydeklarációk és kifejezések között:
A fenti példában látható, hogy a függvénykifejezés mindig más kódszerkezetek részeként szerepel, például változó deklarációkban vagy más függvények argumentumaiként.
A függvénykifejezések különlegessége, hogy használhatjuk őket azonnal, miután definiáltuk őket, és nem szükséges őket külön hivatkozni. Ilyen típusú kifejezés az "azonnali függvénykifejezés" (IIFE, Immediate Invoked Function Expression). A következő példában láthatjuk, hogyan használhatunk egy IIFE-t:
Ez a konstrukció lehetővé teszi, hogy a függvényt azonnal meghívjuk a deklarálás után, anélkül, hogy külön változót vagy függvénynevet rendelünk hozzá.
A függvénykifejezéseknél gyakran találkozunk olyan szintaktikai trükkökkel, mint a +, -, ! vagy ~ operátorok, amelyek segítenek a JavaScript motorjának jelezni, hogy itt nem egy egyszerű függvény deklarációval van dolgunk, hanem egy kifejezéssel. Például:
Ezek a kódminták mind ugyanazt az eredményt érik el, de különböző szintaktikai eszközöket használnak annak biztosítására, hogy a JavaScript megfelelően értelmezze azokat függvénykifejezésként.
A függvények JavaScript-ben tehát rendkívül rugalmasak és erőteljesek. A függvénydeklarációk és kifejezések közötti különbségek megértése alapvető fontosságú, ha hatékonyan szeretnénk használni a nyelvet, és tisztában kell lennünk azokkal a szintaktikai szabályokkal, amelyek befolyásolják a kód működését.
Hogyan kezeli a JavaScript a változókat és a függvényeket a futási környezetben?
A JavaScript nyelv egyik különleges és sokszor félreértett tulajdonsága, hogy miként kezeli a változókat és függvényeket a különböző futási környezetekben. A futási hibák elkerülésére gyakran használhatjuk az ESLint no-use-before-define szabályát, amely biztosítja, hogy a változók mindig a felhasználásuk előtt legyenek deklarálva. Azonban a dolog igazán izgalmasá válik, amikor a függvények deklarációjáról beszélünk.
A függvények deklarációja, amelyet az function functionName szintaxis határoz meg, egy olyan sajátossággal bír, hogy mindig úgy kezelik őket, mintha azok a kód elején lettek volna deklarálva. Ez azt jelenti, hogy a függvényeket bárhol elérhetjük ugyanazon a szinten belül, még akkor is, ha azok később kerülnek definiálásra. Ezt a jelenséget hoistingnak nevezzük. A hoisting olyan viselkedést eredményez, amely ellentmond a futás logikai sorrendjének, és gyakran zavaróan hat azokra, akik nem ismerik ezt a működést.
Például, ha a következő kódot futtatjuk:
A kimenet a következő lesz: Ossu! Itt a greetNinja() függvényhívás sikeresen lefut, még akkor is, hogy a hívás előtt nem került deklarálásra a függvény. A hoisting tulajdonságának köszönhetően a JavaScript motor úgy kezeli a kódot, mintha a függvények deklarációja már a kód elején ott lett volna, miközben a tényleges végrehajtásuk később történik.
Ez a viselkedés csak a függvénydeklarációkra érvényes. Más típusú függvények, például a függvénykifejezések, nem rendelkeznek ilyen furcsa tulajdonsággal. Továbbá, a hoisting nem csak a függvényekre vonatkozik, hanem más JavaScript konstrukciókra is, például az importálásra, amennyiben szigorú módban dolgozunk. Az importálás szintén hoisting hatása alá esik, és ebben a témában többet megtudhatunk a 13. fejezetben.
A következő fontos rész, amit érdemes megérteni, hogy hogyan működik a JavaScript motor, amikor a kódot értelmezi és végrehajtja. A JavaScript motor két fázisban dolgozza fel a kódot: először a szintaktikai ellenőrzést végzi el, hogy észlelje az esetleges hibákat, majd a teljes kódot bytekóddá alakítja. A szintaktikai ellenőrzés során a JavaScript motor már előre felismeri a szintaktikai hibákat, még mielőtt a kód végrehajtásra kerülne. Ez az ellenőrzés gyors, de a teljes elemzés már lassabb, mivel ilyenkor építi fel a változók és függvények hatókörét.
Egy egyszerű példát nézve:
Ez a kód szintaktikai hibát eredményez, mivel ugyanazt a változónevet próbáljuk kétszer deklarálni ugyanabban a hatókörben. A JavaScript motor a szintaktikai ellenőrzés során észleli ezt, és nem engedi végrehajtani a kódot. Ezen kívül, a szintaktikai ellenőrzés sebessége sokkal gyorsabb, mint a teljes kódfeldolgozás, ami segít csökkenteni a futási idő kezdeti késését.
A JavaScript motorok gyakran alkalmaznak optimalizációs technikákat is, hogy javítsák a teljesítményt, például a "lazy parsing" vagy késleltetett elemzést. Ezen technika lényege, hogy a kódot nem elemzik teljesen azonnal, hanem a végrehajtás elindítása után fokozatosan dolgoznak fel egyes részeket, így gyorsabbá válik a program indítása, különösen nagyobb programok esetén. Ez a megközelítés különösen hasznos lehet olyan esetekben, amikor egy nagy függvény végrehajtása jelentős időt venne igénybe.
Például egy nagy JavaScript CLI alkalmazás esetén:
A kód inicializálásakor a JavaScript motor az összes kódot először ellenőrzi és elemzi. Ha ezt a logikát egy külön függvénybe helyezzük, akkor a program gyorsabban indul el, mivel a motor nem szükséges minden egyes parancsot azonnal feldolgoznia. Az eseményhurok inaktív állapotában a háttérben is folytatódhat a kód teljes elemzése, ami gyorsítja az alkalmazás válaszidejét.
Amikor a programban globális változókat hozunk létre, fontos figyelembe venni, hogy a JavaScript korábbi verzióiban könnyen létrejöhettek nem szándékos globális változók, ha nem használunk megfelelő deklarációs kulcsszavakat, mint a var, let vagy const. Ma már a szigorú mód biztosítja, hogy ne lehessen olyan változót hozzárendelni, amely nincs előzetesen deklarálva. Azonban, ha globális változót kell létrehoznunk, akkor azokat közvetlenül a globalThis objektumhoz is rendelhetjük, így azok bárhonnan elérhetők lesznek a programban.
Ezek az alapvető működési mechanizmusok fontos szerepet játszanak a JavaScript teljesítményében és viselkedésében. Az optimalizációk, mint a "lazy parsing", a hoisting, valamint a globális objektum kezelésének megértése segít abban, hogy hatékonyabban dolgozhassunk a nyelvvel és elkerüljük a váratlan futási hibákat.
Hogyan működnek a Promise metódusok és mikor használjuk őket a JavaScript-ben?
A Promise objektumok az aszinkron műveletek kezelésére szolgálnak, és a JavaScript egyik legfontosabb eszközévé váltak. Az alapvető fogalmakat már bemutattuk, de most nézzük meg részletesebben a Promise néhány érdekesebb metódusát és azokat az eseteket, amikor különösen hasznosak.
A Promise.any egy olyan metódus, amely egy olyan ígéretet ad vissza, amely akkor teljesül, amikor az első Promise teljesül. Ha minden Promise visszautasítja a teljesítést, akkor a visszaadott Promise elutasításra kerül, és egy tömböt ad vissza, amely tartalmazza az összes elutasítási okot. Ezzel szemben a Promise.race egy olyan ígéretet ad vissza, amely akkor teljesül, amikor az első Promise bármilyen állapotot elér, akár teljesül, akár elutasításra kerül. A Promise.any bár újabb és ritkábban használt, rendkívül hasznos lehet olyan helyzetekben, amikor többféle módon szeretnénk egy feladatot végrehajtani. Például, ha egy adatbázishoz és egy cache-hez egyszerre küldünk fetch kéréseket. Ha a cache válaszol, akkor tökéletes, ha nem, az sem gond, amíg az adatbázis válasza nem utasítja el.
A Promise.race viszont gyakran arra szolgál, hogy időkorlátot szabjunk egy másik ígéretnek. Az alábbi kód például egy időtúllépés implementálására szolgál, amely a fetch kérésre egy másik Promise-t helyez el, és a gyorsabb eredményt adja vissza:
Ebben a példában az AbortController lehetővé teszi az aszinkron műveletek leállítását, amelyet az ES2018-ban vezettek be. Az AbortController egy rendkívül hasznos API, mivel nélküle a fetch kérés a háttérben folytatódna, miután az időkorlát lejárt, ezzel fölöslegesen felhasználva a rendszer erőforrásait.
Azonban a Promise metódusok, mint például a Promise.any és a Promise.race, rendelkeznek egy fontos korláttal. Mivel ezek csak egyetlen Promise-t adnak vissza, nem alkalmasak olyan helyzetek kezelésére, amikor több, egyszerre zajló aszinkron műveletet kell koordinálni. Például, ha több Web Worker-t szeretnénk egyszerre elindítani, hogy különböző feladatokat hajtsanak végre, de egyszerre túl sok munkát indítani nem praktikus, mert túl sok memóriát használhatunk fel. Erre kínál megoldást a p-limit nevű népszerű könyvtár, amely lehetővé teszi a párhuzamos feladatok számának korlátozását.
A Promise-ok időzítésének megértése is fontos ahhoz, hogy helyesen alkalmazzuk őket. A JavaScript event loop-ja, amely egyetlen szálon működik, soha nem szakítja meg a kód végrehajtását, hanem az eseményeket sorba rendezi és azokat feldolgozza. Az események feldolgozása kétféle sorba történik: a mikrotask sorba és a makrotask sorba. A mikrotask sorba azok az események kerülnek, amelyek a Promise-ok then metódusaitól származnak. Ezzel szemben a makrotask sorba tartoznak a setTimeout vagy a DOM események, amelyek késleltetve kerülnek végrehajtásra.
Ez a prioritásrendszer lehetővé teszi, hogy a Promise visszahívása mindig hamarabb végrehajtódjon, mint a setTimeout-ok, még akkor is, ha azok nulla milliszekundumos késleltetést kaptak. Az események feldolgozásának sorrendje a következőképpen működik:
-
A JavaScript motor végrehajtja a kódot.
-
Ha van valami a mikrotask sorban, az első mikrotask végrehajtódik.
-
Ha nincs mikrotask, a makrotask sorban lévő események kerülnek feldolgozásra.
Ez az eseménykezelési mechanizmus alapvetően megváltoztatja a kódot, és sokkal könnyebbé teszi annak előrejelzését. Mivel a Promise-ok mikrotask-ként kerülnek végrehajtásra, a then metódus nem hívódik meg szinkron módon, hanem mindig aszinkron módon, garantálva ezzel, hogy a Promise-on belüli kód mindig a következő szintű esemény előtt fusson le.
Ezen kívül, az ECMAScript 2015 óta a Promise-ok pontos időzítése és a mikrotask kezelés is meghatározottá vált. Az ECMAScript szabvány nem definiálja a setTimeout vagy queueMicrotask időzítését, de az HTML Living Standard biztosítja, hogy a JavaScript motorok követni fogják a pontos időzítési szabályokat.
Így a Promise-ok hatékonyan segítenek az aszinkron műveletek kezelésében, és biztosítják a kód olvashatóságát és előrejelezhetőségét. Az await kulcsszó egy egyszerűsített szintaxis formájában hozza el a Promise-ok erejét, és a then visszahívásait egyetlen sorban történő szinkron kódokként oldja meg.
Miért fontos elkerülni az "any" típus használatát TypeScript-ben?
A TypeScript egyik alapvető ereje a típusellenőrzés, amely segít a hibák korai felismerésében és megelőzésében a kód fejlesztése során. Azonban a "any" típus, amelyet akkor használunk, ha a típus nem ismert vagy nem specifikált, gyengítheti a típusellenőrzés hatékonyságát. Bár hasznos lehet a meglévő JavaScript kód migrálásakor, a TypeScript-hez való teljes körű átállás során inkább el kell kerülni.
Az "any" típus alkalmazásakor a típusellenőrzés gyakorlatilag ki van kapcsolva, ami azt jelenti, hogy bárminek átadhatunk bármit, és a rendszer nem fog figyelmeztetni, ha hibás típusú adatot próbálunk felhasználni. Egy példát véve, az alábbi kódban:
A változókat, mint a str és count, nem adtuk meg típusokkal, ezért a TypeScript típusellenőrzője nem tudja megállapítani, milyen típusú értékek lehetnek, így visszaesik az "any" típusra. Ebben az esetben a rendszer nem hajt végre semmilyen típusellenőrzést, és nem jelez hibát, ha a str nem egy string, vagy ha a count nem egy szám.
A legjobb gyakorlat, hogy explicit módon megadjuk a típusokat a paramétereknél, így:
Ez lehetővé teszi a típusellenőrzés működését, és ha a függvényen belüli vagy azon kívüli kódban nem megfelelő típusú adatot adunk át, a TypeScript hibaüzenetet generál. A típus annotációk emellett biztosítják, hogy a pluralize függvény mindig egy string értéket adjon vissza.
Alternatív megoldás, hogy a függvény típusát is annotá
Hogyan forgathatjuk a 3D objektumot az érintés helyzete alapján Androidon OpenGL ES segítségével?
Mi a leghatékonyabb módja az öregedés lassításának a mozgás és táplálkozás révén?
Milyen módon alakíthatjuk át a bűnügyi történetek struktúráját, hogy még inkább izgalmasak legyenek?

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский