2. blokk · Építőkövek · 7. fejezet
A tömb sorszámozott listát tárol; az objektum egy dolog nevesített tulajdonságait fogja össze
kulcs–érték párokként: egy termék neve, ára, készlete egy helyen. Ez a JavaScript központi adatszerkezete — minden
API-válasz, minden req.body, minden konfiguráció objektum. A fejezet végén objektumok tömbjével,
a webshop terméklistájával dolgozol.
Object.assign).Egy terméket eddig három külön változóval írtunk le (nev, ar, keszlet) —
tíz terméknél ez harminc változó, és semmi nem köti össze őket. Az objektum egyetlen értékbe csomagolja az összetartozó
adatokat. Kapcsos zárójellel hozzuk létre (ez az objektumliterál), a kulcs: érték párokat vesszővel választjuk el:
const termek = { nev: "Vezeték nélküli egér", // kulcs: nev, érték: szöveg ar: 6990, // szám keszlet: 40, akcios: false, // logikai cimkek: ["periféria", "vezeték nélküli"] // tömb is lehet érték }; console.log(termek); // { nev: 'Vezeték nélküli egér', ar: 6990, keszlet: 40, akcios: false, cimkek: [ 'periféria', 'vezeték nélküli' ] } const ures = {}; // üres objektum — később töltjük fel
| Tömb | Objektum | |
|---|---|---|
| Mit tárol | sok hasonló dolgot sorban | egy dolog több tulajdonságát |
| Elérés | index: t[0] | kulcs: o.nev |
| Sorrend | számít | nem lényeges |
| Példa | [6990, 45990, 1990] | { nev: "Egér", ar: 6990 } |
A kulcs mindig szöveg (az idézőjel elhagyható, ha érvényes azonosító); az érték bármi lehet — szám, szöveg, logikai, tömb, másik objektum, sőt függvény is.
Egy tulajdonságot kétféleképpen érhetünk el: pont-jelöléssel (obj.kulcs) vagy
szögletes zárójellel (obj["kulcs"]):
const termek = { nev: "Egér", ar: 6990, "szállítási idő": "2 nap" }; console.log(termek.nev); // Egér — pont: a gyakoribb, olvashatóbb console.log(termek["ar"]); // 6990 — szögletes zárójel + kulcs szövegként console.log(termek["szállítási idő"]); // 2 nap — szóközös kulcs: CSAK így érhető el let mezo = "ar"; console.log(termek[mezo]); // 6990 — DINAMIKUS kulcs: a változó értéke a kulcs console.log(termek.mezo); // undefined — ez a "mezo" NEVŰ kulcsot keresné! console.log(termek.szin); // undefined — nincs ilyen tulajdonság, de NEM hiba
Pont, ha a kulcsot kódírás közben ismered (termek.ar). Szögletes zárójel, ha a kulcs
egy változóban van (termek[mezo]), vagy szóközt/kötőjelet tartalmaz. A szótár-mintánál (8. szakasz)
a dinamikus kulcs lesz a főszereplő.
Nem létező tulajdonság olvasása csendben undefined-ot ad. Ha viszont ebből próbálsz továbbolvasni
(termek.szin.length), azt kapod: Cannot read properties of undefined. Ez a leggyakoribb
JavaScript-hibaüzenet — ha látod, valamelyik köztes érték nem az, amire számítottál (elgépelt kulcs?).
Az objektum a létrehozása után is alakítható: meglévő tulajdonság értéke átírható, új tulajdonság egyszerű
értékadással jön létre, a delete pedig eltávolít egyet:
const termek = { nev: "Egér", ar: 6990 }; termek.ar = 5990; // meglévő módosítása termek.keszlet = 12; // ÚJ tulajdonság: egyszerűen értéket adunk neki termek["gyarto"] = "Logi"; // szögletes zárójellel is console.log(termek); // { nev: 'Egér', ar: 5990, keszlet: 12, gyarto: 'Logi' } delete termek.gyarto; // tulajdonság törlése console.log(termek); // { nev: 'Egér', ar: 5990, keszlet: 12 }
console.log("ar" in termek); // true console.log("gyarto" in termek); // false — töröltük console.log(termek.hasOwnProperty("nev")); // true — ugyanez metódussal // Miért nem elég a termek.gyarto !== undefined? const o = { kedvezmeny: undefined }; console.log(o.kedvezmeny !== undefined); // false — pedig a kulcs LÉTEZIK console.log("kedvezmeny" in o); // true — az in a kulcs létét nézi, nem az értékét
A gyakorlatban az in operátor a kényelmes; a hasOwnProperty abban különbözik, hogy csak az objektum
saját tulajdonságait nézi (az örökölteket nem — erről a 12. fejezetben lesz szó).
Ha egy tulajdonság értéke függvény, azt metódusnak hívjuk — ilyen a push a tömbön vagy a
round a Math-on. Saját objektumba is tehetünk metódust, rövid írásmóddal. A metódus törzsében
a this arra az objektumra mutat, amelyen a metódust meghívták — így a metódus eléri az
objektum többi tulajdonságát:
const kosar = { tetelek: 3, osszeg: 20970, kiir() { // metódus (rövid alak: nem kell "function") return `${this.tetelek} tétel, ${this.osszeg} Ft`; // this = kosar }, hozzaad(ar) { this.tetelek++; // az objektum saját adatát módosítja this.osszeg += ar; } }; kosar.hozzaad(1990); console.log(kosar.kiir()); // 4 tétel, 22960 Ft
Az arrow functionnek nincs saját this-e — a környezetéből örökli, így kiir: () => this.tetelek
nem az objektumra mutatna. Metódust ezért a fenti rövid alakkal (kiir() { … }) vagy
function-nal írj, arrow-t pedig callbackekhez használj. A this részleteire a 12. fejezetben,
az osztályoknál térünk vissza — most ennyi elég.
Egy tulajdonság értéke lehet másik objektum vagy tömb — így épülnek fel a valódi adatszerkezetek. Az elérés egyszerűen láncolódik:
const rendeles = { azonosito: 1042, vevo: { nev: "Kiss Anna", cim: { varos: "Szeged", irsz: "6720" } }, tetelek: ["Egér", "Kábel"], fizetve: true }; console.log(rendeles.vevo.nev); // Kiss Anna console.log(rendeles.vevo.cim.varos); // Szeged — objektum az objektumban console.log(rendeles.tetelek[1]); // Kábel — tömb az objektumban, index console.log(rendeles.tetelek.length); // 2 rendeles.vevo.cim.varos = "Pécs"; // mélyen is módosítható
A két szerkezet ereje együtt mutatkozik meg: egy tömb, amelyben objektumok vannak. Így tároljuk a termékeket, a rendeléseket, a felhasználókat. A tömböt a 6. fejezet szerint járjuk be, és minden elem egy objektum, amelynek a tulajdonságait olvassuk:
const termekek = [ { id: 1, nev: "Egér", ar: 6990, keszlet: 40 }, { id: 2, nev: "Monitor", ar: 45990, keszlet: 0 }, { id: 3, nev: "Laptop", ar: 289990, keszlet: 5 } ]; console.log(termekek[1].nev); // Monitor — 1. elem, nev tulajdonság // ÖSSZEGZÉS objektumokon: a raktárkészlet értéke let raktarErtek = 0; for (const t of termekek) { raktarErtek += t.ar * t.keszlet; } console.log(raktarErtek); // 1729550 // KERESÉS objektumokon: termék id szerint (vagy null) function keresId(lista, id) { for (const t of lista) { if (t.id === id) return t; // az egész objektumot adjuk vissza } return null; // "nincs találat" — null, nem undefined } console.log(keresId(termekek, 3).nev); // Laptop console.log(keresId(termekek, 9)); // null // ÚJ TÖMB FELÉPÍTÉSE: csak a raktáron lévők const raktaron = []; for (const t of termekek) { if (t.keszlet > 0) raktaron.push(t); } console.log(raktaron.length); // 2
Az API-k JSON-objektumokkal kommunikálnak — a JSON szintaxisa szinte betűre az objektumliterál. Express-ben a beérkező
POST kérés törzse, a req.body, egy ilyen objektum, a GET /termekek/:id
pedig a fenti keresId — ha null, 404-et küldünk:
app.post('/termekek', (req, res) => { const uj = { id: kovetkezoId++, nev: req.body.nev, ar: req.body.ar, keszlet: req.body.keszlet }; termekek.push(uj); // objektum be a tömbbe res.status(201).json(uj); // objektum vissza JSON-ként });
Ahogy a tömb, az objektum is referencia: a változó nem az objektumot tárolja, hanem rámutat. Értékadással nem másolat készül, hanem két név ugyanarra az objektumra:
const a = { nev: "Egér", ar: 6990 }; const b = a; // NEM másolat b.ar = 1; console.log(a.ar); // 1 — a is "változott": ugyanaz az objektum console.log(a === b); // true const c = Object.assign({}, a); // SEKÉLY MÁSOLAT: új üres objektumba átmásolja a tulajdonságokat c.ar = 9999; console.log(a.ar, c.ar); // 1 9999 — függetlenek console.log(a === c); // false — két külön objektum, azonos tartalommal // Object.assign több forrásból is másol — a későbbi felülírja a korábbit: const frissitett = Object.assign({}, a, { ar: 5990, akcios: true }); console.log(frissitett); // { nev: 'Egér', ar: 5990, akcios: true }
A const csak azt tiltja, hogy a változó másik objektumra mutasson. A tulajdonságok szabadon írhatók:
termek.ar = 5990 rendben van const termek mellett is. Ezért a feladatban, amely „módosítsd
az objektumot és add vissza", ugyanazt az objektumot alakítod és return-özöd — ahol viszont „új objektumot"
kérünk, ott Object.assign({}, …) vagy egy új {} feltöltése a megoldás.
„Sekély" a másolat, mert csak a legfelső szintet másolja: ha egy tulajdonság értéke maga is objektum vagy tömb, arra az új objektum is
ugyanúgy hivatkozik. A 9. fejezetben a spread ({ ...a }) ad ugyanerre rövidebb írásmódot, a mély
másolásról pedig a 11. fejezetben lesz szó.
A tömböt indexszel járjuk be — az objektumnak nincs indexe, de a kulcsait (vagy értékeit) kikérhetjük egy tömbbe, és azon már mehet a ciklus:
const termek = { nev: "Egér", ar: 6990, keszlet: 40 }; console.log(Object.keys(termek)); // [ 'nev', 'ar', 'keszlet' ] — a kulcsok tömbje console.log(Object.values(termek)); // [ 'Egér', 6990, 40 ] — az értékek tömbje console.log(Object.entries(termek)); // [ [ 'nev', 'Egér' ], [ 'ar', 6990 ], [ 'keszlet', 40 ] ] — párok console.log(Object.keys(termek).length); // 3 — hány tulajdonsága van for (const kulcs of Object.keys(termek)) { console.log(kulcs + ": " + termek[kulcs]); // dinamikus kulcs: itt kell a szögletes zárójel! } // nev: Egér / ar: 6990 / keszlet: 40
A for…in ciklus közvetlenül a kulcsokon megy végig — ugyanaz, mint a for…of az
Object.keys-en, csak rövidebb. Ne keverd a kettőt: for…of → tömb elemei,
for…in → objektum kulcsai:
for (const kulcs in termek) { console.log(kulcs, "=", termek[kulcs]); } // Másolás kézzel — így működik belül az Object.assign: function masol(o) { const uj = {}; for (const k in o) { uj[k] = o[k]; // dinamikus kulccsal írunk az új objektumba } return uj; }
| Eszköz | Mit ad / mit jár be | Tipikus használat |
|---|---|---|
Object.keys(o) | kulcsok tömbje | hány mező van; kulcs szerinti bejárás |
Object.values(o) | értékek tömbje | értékek összegzése, pl. egy számláló összesítése |
Object.entries(o) | [kulcs, érték] párok tömbje | kulcs és érték együtt kell (10. fejezettől map-pel) |
for (const k in o) | kulcsok, egyenként | átmásolás, átalakítás új objektumba |
Az objektumot nem csak „egy dolog leírására" használjuk. Ha a kulcsok futás közben keletkeznek — például kategórianevek, szavak, felhasználónevek —, az objektum egy szótár (kulcs → érték táblázat). A klasszikus feladat a számlálás kulcsonként: hány termék van kategóriánként?
const termekek = [ { nev: "Egér", kategoria: "periferia" }, { nev: "Monitor", kategoria: "kijelzo" }, { nev: "Billentyűzet", kategoria: "periferia" }, { nev: "Webkamera", kategoria: "periferia" } ]; const szamlalo = {}; // üres szótárral indulunk for (const t of termekek) { const k = t.kategoria; // a kulcs az adatból jön → szögletes zárójel if (k in szamlalo) { szamlalo[k]++; // már láttuk: növeljük } else { szamlalo[k] = 1; // első előfordulás: új kulcs, 1-ről indul } } console.log(szamlalo); // { periferia: 3, kijelzo: 1 } for (const k in szamlalo) { // az eredmény kiolvasása console.log(`${k}: ${szamlalo[k]} db`); }
1) üres {}; 2) a ciklusban a kulcsot az adatból veszed, és szögletes zárójellel írsz
(szamlalo[k], nem szamlalo.k!); 3) „ha már van, frissítsd; ha nincs, hozd létre".
Ugyanígy készül ár-összeg kategóriánként, csoportosítás, gyors keresőtábla id szerint. A 11. fejezet Map-je
ugyanezt tudja, kicsit másképp.
Kísérletezz az objektumokkal: adj hozzá és törölj tulajdonságot, próbáld ki a dinamikus kulcsot, nézd meg, mi történik két változóval,
amely ugyanarra az objektumra mutat, és építs szótárat. 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) ↓
Egy megadott nevű függvényt írsz; az ellenőrző meghívja, és a visszaadott értéket hasonlítja a várthoz.
Objektumnál ez azt jelenti: pontosan a kért kulcsok legyenek benne (se több, se kevesebb), a megfelelő értékekkel — a kulcsok
sorrendje nem számít. A paraméterek termékobjektumok ({ nev, ar, keszlet }) vagy ilyenek tömbje. Figyeld a leírást:
hol kell új objektumot visszaadni, és hol a kapottat módosítani. Csak az eddig tanultakat használd (ciklusok, tömb-alapok,
Object.keys/values/entries, Object.assign, for…in).
⭐ alap · ⭐⭐ haladó · ⭐⭐⭐ kihívás — a haladásod ebben a böngészőben megmarad.
Ugyanaz a négy minta, mint a 6. fejezetben (összegzés, számlálás, legjobb-eddig, új tömb felépítése) — csak a ciklusváltozó egy
objektum, és a t.ar, t.keszlet tulajdonságával számolsz. Ha a „legdrágábbat" kérik, az egész
objektumot jegyezd meg, ne csak az árát.
{ nev: "Egér", ar: 6990 }. Egy dolog tulajdonságait fogja össze; az érték bármi lehet (tömb, objektum, függvény is).o.kulcs (ismert kulcs) vagy o["kulcs"] / o[valtozo] (dinamikus vagy szóközös kulcs). Nem létező tulajdonság → undefined.delete o.kulcs. Létezés: "kulcs" in o vagy o.hasOwnProperty("kulcs").kiir() { … }); benne a this az objektum. Arrow functionnek nincs saját this-e — metódusnak ne használd.rendeles.vevo.cim.varos); objektumok tömbje a webshop adatformátuma — a 6. fejezet mintái objektum-tulajdonságokkal (t.ar * t.keszlet).b = a ugyanarra mutat; Object.assign({}, a) sekély másolat, Object.assign({}, a, b) összefésül (b felülír). A const objektum tartalma módosítható.Object.keys / values / entries tömböt ad; for (const k in o) a kulcsokon megy. Szótár-minta: üres {}, kulcs az adatból, o[k]-val írsz — „ha van, frissítsd, ha nincs, hozd létre".Az adatszerkezetek megvannak: függvény, tömb, objektum. A következő fejezetben a szövegkezelést vesszük sorra — a
string-metódusokat (toUpperCase, includes, slice,
split, trim…), amelyekkel a felhasználótól érkező adatokat tisztítjuk és ellenőrizzük.
Sok metódus neve ismerős lesz a tömbökből — nem véletlenül.