3. blokk · Modern JS · 9. fejezet
A JavaScript 2015-ben (ES6) és azóta minden évben kapott olyan rövidítéseket, amelyek nem új képességet adnak, hanem a mindennapi műveleteket teszik tömörebbé: értékek kiszedése tömbből és objektumból, másolás, összefésülés, biztonságos mélyre nyúlás. Egy mai Express-kód minden sorában ott van valamelyik — nélkülük olvasni sem tudnád. Ebben a fejezetben mindet megtanulod, és rögtön látod, hol jön elő a szerveren.
...) operátorral másolsz, összefűzöl és „módosított másolatot" készítesz; a rest paraméterrel bármennyi argumentumot fogadsz.?.) segítségével, és tudod, miben más a ?? a ||-tól.Nézd meg ezt a részletet egy tipikus Express-útvonalból — még nem kell értened a keretrendszert, csak a szintaxist figyeld:
app.post("/termekek/:id", (req, res) => { const { id } = req.params; // ← destrukturálás const { nev, ar = 0 } = req.body; // ← destrukturálás alapértékkel const regi = termekek.find(t => t.id === id); const uj = { ...regi, nev, ar }; // ← spread + rövidített tulajdonságnév const varos = uj.szallitas?.cim?.varos ?? "—"; // ← optional chaining + nullish coalescing res.json(uj); });
Öt sorban öt „modern" elem. Ezek mind rövidítések: mindegyik leírható a régi módon is, csak hosszabban.
A fejezet végén ezt a kódot folyékonyan olvasod. Egy mondat ismétlés a 2. fejezetből: változót const-tal
(ha nem változik) vagy let-tel deklarálunk — a var-t a modern kódban nem használjuk.
A destrukturálás egy tömb elemeit egyetlen lépésben külön változókba teszi. Bal oldalon a változók „mintája" áll szögletes zárójelben, jobb oldalon a tömb — pozíció szerint párosít:
const pont = [47.5, 19.0]; const szelesseg = pont[0]; const hosszusag = pont[1];
const pont = [47.5, 19.0]; const [szelesseg, hosszusag] = pont; // szelesseg = 47.5, hosszusag = 19.0
Elemeket ki lehet hagyni (üres hely vesszővel), alapértéket lehet adni a hiányzó elemeknek, és a klasszikus „csere" is egy sor lesz:
const [, masodik] = ["első", "második", "harmadik"]; console.log(masodik); // "második" — az elsőt kihagytuk const [a, b, c = 0] = [1, 2]; console.log(a, b, c); // 1 2 0 — c-nek nincs párja, az alapértéket kapja let x = 1, y = 2; [x, y] = [y, x]; // csere segédváltozó nélkül console.log(x, y); // 2 1 const [nev, kiterjesztes] = "kep.png".split("."); // split eredményét rögtön szétszedjük console.log(nev, kiterjesztes); // kep png
Objektumnál a minta kapcsos zárójelben áll, és kulcsnév szerint párosít (a sorrend nem számít). Ez a leggyakoribb forma — egy objektum mezőit egy lépésben változókká tesszük:
const termek = { nev: "Egér", ar: 6990, keszlet: 3 }; const nev = termek.nev; const ar = termek.ar;
const termek = { nev: "Egér", ar: 6990, keszlet: 3 }; const { nev, ar } = termek; // nev = "Egér", ar = 6990 (keszlet nem kell → nem vesszük ki)
Három bővítés: átnevezés (kulcs: ujNev), alapérték hiányzó mezőnek (kulcs = ertek),
és beágyazott objektum kibontása:
const termek = { nev: "Egér", ar: 6990, meret: { szel: 6, hossz: 11 } }; const { ar: egysegar } = termek; // az ar mezőt egysegar néven vesszük ki console.log(egysegar); // 6990 (ar nevű változó NEM jön létre) const { akcios = false, keszlet = 0 } = termek; // nincsenek ilyen mezők → alapértékek console.log(akcios, keszlet); // false 0 const { meret: { szel, hossz } } = termek; // beágyazott: a meret belsejéből console.log(szel, hossz); // 6 11 (meret nevű változó nem jön létre)
A destrukturálás a paraméterlistában is működik: ha a függvény egy objektumot kap, a fejlécében rögtön kibonthatod,
amire szükséged van — így a törzsben nem kell termek.nev-et írni:
function leiras({ nev, ar, keszlet = 0 }) { return `${nev}: ${ar} Ft (${keszlet} db)`; } console.log(leiras({ nev: "Egér", ar: 6990 })); // Egér: 6990 Ft (0 db) // arrow függvénnyel is: const nevKiir = ({ nev }) => console.log(nev); nevKiir({ nev: "Monitor", ar: 45990 }); // Monitor
Express-ben minden útvonal-kezelő első sorai destrukturálnak: const { id } = req.params; az URL-ből
(/termekek/:id), const { nev, ar = 0 } = req.body; a beküldött JSON-ból,
const { oldal = 1, limit = 20 } = req.query; a ?oldal=2 lekérdezésből — alapértékkel,
hogy hiányzó paraméternél is működjön. Modulok betöltésénél is ez van: const { readFile } = require("fs");.
A spread operátor (...) egy tömböt vagy objektumot „kiterít": az elemeit/mezőit egyenként odaírja,
ahol áll. Tömbnél ez másolást, összefűzést és „tömb → külön argumentumok" átalakítást ad:
const a = [1, 2, 3]; const b = [4, 5]; const masolat = [...a]; // [1, 2, 3] — ÚJ tömb, nem ugyanaz a hivatkozás const egyben = [...a, ...b]; // [1, 2, 3, 4, 5] const bovitett = [0, ...a, 99]; // [0, 1, 2, 3, 99] console.log(Math.max(...a)); // 3 — Math.max(1, 2, 3): külön argumentumok kell neki console.log(Math.max(a)); // NaN — tömböt nem tud kezelni
Objektumnál ugyanez a jel a mezőket másolja át. A sorrend számít: ha ugyanaz a kulcs kétszer szerepel, a később írt nyer — ezért a spread után írt mező felülír. Ez a „módosított másolat" minta, a modern JS egyik legfontosabb fogása:
const termek = { nev: "Egér", ar: 6990, keszlet: 3 }; const masolat = { ...termek }; // új objektum, ugyanazok a mezők const akcios = { ...termek, ar: 4990 }; // másolat, de az ar felülírva const cimkezett = { ...termek, kategoria: "periféria" }; // másolat + új mező console.log(akcios.ar); // 4990 console.log(termek.ar); // 6990 — az EREDETI nem változott! // két objektum összefésülése: alapértékek + a felhasználó választása const alap = { szin: "kék", meret: "M", db: 1 }; const valasztas = { meret: "L" }; console.log({ ...alap, ...valasztas }); // { szin: "kék", meret: "L", db: 1 } console.log({ ...valasztas, ...alap }); // { meret: "M", szin: "kék", db: 1 } — fordítva az alap nyer!
A spread sekély másolat: ha az objektumban egy másik objektum vagy tömb van (pl. meret: { szel, hossz }),
annak csak a hivatkozása másolódik — a belső objektum közös marad. Egyszerű, „lapos" adatoknál ez nem gond; beágyazottnál
a belső szintet is spread-elni kell: { ...termek, meret: { ...termek.meret, szel: 7 } }.
Ugyanaz a három pont a bal oldalon (paraméterlistában vagy destrukturáló mintában) az ellenkezőjét csinálja: összegyűjti a „többit" egy tömbbe. Rest paraméterrel a függvény bármennyi argumentumot fogad:
function osszeg(...szamok) { // szamok: közönséges tömb az összes argumentummal let s = 0; for (const x of szamok) { s += x; } return s; } console.log(osszeg(1, 2, 3, 4)); // 10 console.log(osszeg()); // 0 — üres tömb function naplo(szint, ...uzenetek) { // az első kötelező, a többi a rest-be megy console.log(`[${szint}]`, uzenetek.join(" ")); } naplo("INFO", "szerver", "elindult"); // [INFO] szerver elindult
Destrukturálásban a rest az első néhány elem után a maradékot gyűjti tömbbe (tömbnél), illetve a ki nem vett mezőket objektumba (objektumnál):
const [elso, ...tobbi] = [10, 20, 30, 40]; console.log(elso, tobbi); // 10 [ 20, 30, 40 ] const { jelszo, ...nyilvanos } = { nev: "anna", email: "a@b.hu", jelszo: "titok" }; console.log(nyilvanos); // { nev: "anna", email: "a@b.hu" } — a jelszó nélkül
Ugyanaz a ... jel. Ha jobb oldalon / hívásban áll ([...a], f(...t)),
kiterít → spread. Ha bal oldalon / paraméterlistában áll (const [x, ...r] =,
function f(...args)), összegyűjt → rest. A rest csak az utolsó helyen állhat.
Három apró kényelmi szintaxis objektumliterálokhoz. A leggyakoribb a rövidített tulajdonságnév: ha a kulcs neve ugyanaz, mint a változóé, elég egyszer leírni:
const nev = "Egér"; const ar = 6990; const regi = { nev: nev, ar: ar }; // régi mód const uj = { nev, ar }; // ugyanaz rövidítve → { nev: "Egér", ar: 6990 } function termekLetrehoz(nev, ar, keszlet = 0) { return { nev, ar, keszlet }; // paraméterekből objektum egy sorban }
A számított kulcs ([kifejezés]: érték) akkor kell, ha a kulcs neve egy változóban van. A
metódus-rövidítés pedig a nev: function() {} helyett nev() {}:
const mezo = "keszlet"; const o = { [mezo]: 12 }; // { keszlet: 12 } — a kulcs a mezo változó értéke const o2 = { ["ar_" + "eur"]: 18 }; // { ar_eur: 18 } — bármilyen kifejezés lehet const kosar = { tetelek: [], hozzaad(termek) { // metódus-rövidítés (= hozzaad: function(termek) {…}) this.tetelek.push(termek); }, darab() { return this.tetelek.length; } }; kosar.hozzaad("Egér"); console.log(kosar.darab()); // 1
Ha egy láncban valahol undefined vagy null áll, a további pont hibát dob —
ez a JavaScript leggyakoribb futási hibája („Cannot read properties of undefined"). Az optional chaining
(?.) helyett hiba nélkül undefined-ot ad, és a lánc többi része már ki sem értékelődik:
const rendeles = { id: 1, vevo: { nev: "Anna" } }; // nincs cim! // console.log(rendeles.vevo.cim.varos); // ✗ HIBA: cim undefined, .varos-t nem olvashatjuk console.log(rendeles.vevo?.cim?.varos); // undefined — csendben console.log(rendeles.vevo?.nev); // "Anna" — ahol van érték, ott sima pontként működik const semmi = null; console.log(semmi?.barmi?.melyen); // undefined — null-nál is
Működik tömbindexre (t?.[0]) és függvényhívásra is (fn?.() — csak akkor hív, ha a függvény létezik):
const beallitas = { }; beallitas.onKesz?.(); // nincs ilyen függvény → nem történik semmi, hiba sem const tetelek = rendeles.tetelek; // undefined console.log(tetelek?.[0]?.nev); // undefined
Ne szórd tele a kódot ?.-lal! Ahol egy mező biztosan létezik, írj sima pontot — ha ott mégis hiba van,
azt látni akarod. A ?. oda való, ahol az adat valóban opcionális: a vevő megadhatta a címét, de nem kötelező.
Az a ?? b értéke a, ha az nem null és nem undefined;
különben b. „Alapérték, ha hiányzik." Ismerős a 3. fejezetből az a || b, ami hasonlót csinál —
de az minden hamis értéket lecserél, a 0-t és az üres szöveget is. Ez a különbség gyakran okoz hibát:
a értéke | a || "alap" | a ?? "alap" |
|---|---|---|
undefined | "alap" | "alap" |
null | "alap" | "alap" |
0 | "alap" ⚠️ | 0 ✓ |
"" | "alap" ⚠️ | "" ✓ |
false | "alap" ⚠️ | false ✓ |
"Egér", 42 | az érték | az érték |
const keszlet = 0; // tényleg nulla darab van console.log(keszlet || 10); // 10 — ROSSZ: a 0 hamis, ezért „hiányzónak" nézte console.log(keszlet ?? 10); // 0 — JÓ: a 0 érvényes érték const megjegyzes = ""; // a vevő szándékosan üresen hagyta console.log(megjegyzes || "—"); // "—" console.log(megjegyzes ?? "—"); // "" // a tipikus páros: ?. a mélyre nyúláshoz, ?? az alapértékhez const rendeles = { vevo: { nev: "Anna" } }; const varos = rendeles.vevo?.cim?.varos ?? "ismeretlen"; console.log(varos); // "ismeretlen"
Van hozzá értékadó változat is: a ??= b csak akkor ír b-t a-ba,
ha a null/undefined volt — „töltsd ki, ha üres":
let beallitasok = { tema: "sötét" }; beallitasok.tema ??= "világos"; // már van érték → marad "sötét" beallitasok.nyelv ??= "hu"; // nincs → beírja console.log(beallitasok); // { tema: "sötét", nyelv: "hu" }
A szerver indításánál: const port = process.env.PORT ?? 3000; — ha a környezeti változó nincs beállítva, 3000.
A kérés feldolgozásánál: const { nev, ar = 0 } = req.body; majd const kedvezmeny = req.body.kedvezmeny ?? 0;
— itt a || hibás lenne, mert a 0% kedvezményt is „hiányzónak" venné. A válaszban: rendeles.vevo?.email ?? "nincs megadva".
Próbáld ki mindegyik szintaxist! Cseréld meg a spread-ek sorrendjét, adj alapértéket, vedd ki a ?.-t és nézd meg
a hibaüzenetet. A kimenet úgy jelenik meg, ahogy Node-ban a console.log mutatná.
Válassz mintát vagy írj sajátot — majd Futtatás (Ctrl+Enter) ↓
Rövid függvények, amelyek mindegyike egy-egy modern szintaktikai elemet gyakoroltat. Mindegyik megoldható a régi módon is —
de a cél most éppen a tömör, modern írásmód: destrukturálj, spread-elj, használd a ?.-t és a ??-t.
Az Ellenőrzés a visszatérési értéket hasonlítja (objektumnál a mezők pontos halmazát!).
⭐ alap · ⭐⭐ haladó · ⭐⭐⭐ kihívás — a haladásod ebben a böngészőben megmarad.
Ahol a feladat „új objektumot" vagy „új tömböt" kér, tényleg újat készíts ({ ...o, … }, [...t, …]) —
a paraméterként kapott eredetibe ne írj bele. Egy szerveren a kapott adatot sokan használják; aki csendben átírja, mások hibáját okozza.
const [a, b] = t — pozíció szerint; kihagyás vesszővel, alapérték = ertek, csere [a, b] = [b, a].const { nev, ar } = o — kulcs szerint; átnevezés { ar: egysegar }, alapérték { keszlet = 0 }, beágyazott { meret: { szel } }, paraméterlistában is: function f({ nev, ar }).... jobb oldalon): [...a, ...b] másol/összefűz, Math.max(...t) argumentumokká terít; { ...o, ar: 990 } = módosított másolat — a később írt mező nyer. Sekély másolat!... bal oldalon): function f(...args) bármennyi argumentum; const [elso, ...tobbi] = t, const { jelszo, ...nyilvanos } = o.{ nev, ar } (= { nev: nev, ar: ar }), számított kulcs { [kulcs]: ertek }, metódus nev() {}.?. — ha a lánc egy pontja null/undefined, hiba helyett undefined; o?.[i], fn?.() is.?? — alapérték csak null/undefined esetén (a || a 0-t és a ""-t is lecseréli!); a ??= b kitölti, ha üres.const alapból, let ha változik — var soha.A szintaxis megvan — most jön az adatfeldolgozás: a tömb-metódusok (map, filter,
reduce, sort, some/every), amelyekkel egy webshop
termékeit szűrjük, rendezzük, összesítjük és átalakítjuk — ciklus írása nélkül, láncolva. Pontosan ez történik egy API-végpont belsejében.