6 min read

Beépített objektumok első rész

Beépített objektumok első rész

Ebben a cikkben nem az olyan objektumokról lesz szó amiket te hozol létre, majd beépítünk valahova, mint anno Kőmives Kelemen balladájában, hanem azon objektumokról, amiket a nyelv alapból tartalmaz, így azok megalkotásával neked már nem kell foglalkozni.

Az egyik ilyen nagyon fontos és gyakran használt objektum típus a:

Tömbök (Arrays)

A tömb egy olyan lista szerű adatszerkezet (hivatalosan ordered collection, ha angolul társalognál a témáról valakivel), ahol annak elemeit sorszámukra hivatkozva (index) tudjuk elérni. Pont úgy is lehet a legkönnyebben elképzelni, mint pl. egy bevásárló listát, ahol az egyes megvásárolni kívánt dolgokat egymás alá írjuk:
0: Alma
1: Körte
2: Eper

Nyelvtől függően egy tömb lehet fix méretű (a fenti példánál maradva fix számú elemet vehetünk fel a bevásárló listánkra) vagy dinamikus (tetszés szerint bővíthethjük vagy akár csökkenthetjük a lista méretét). Szintén nyelvfüggő, hogy a tömbben tárolt adatok típusára van-e megkötés vagy nincs, azaz csak azonos típusú adatokat tárolhatunk vagy különféléket is. (pl. a fenti listán csak gyümülcsök szerepelnek). A javascript tömbök dinamikusak és nem csak azonos típusú elemei lehetnek. Talán feltűnt, hogy a fenti listát 0-s sorszámmal kezdtem. Ez azért van, mert a tömb első eleme a 0-s index-el érhető el (vannak nyelvek ahol 1-gyel és vannak nyelvek ahol szabadon megválasztható, de javascriptnél 0-tól indul a tömb. Tipp: google a barátod: "arrays start at 1 meme").

Tömböt létrehozni többféleképpen lehet, a legegyszerűbb és leggyakrabban használt forma így néz ki (később megismerjük majd a többi formát is):

var myArray = ["Alma", "Körte", "Eper"]

Mint említettem a tömb elemét az indexe segítségével lehet elérni, annak formája pedig így néz ki:

console.log(myArray[0], myArray[1], myArray[2]) // Alma Körte Eper

Az objektumokról szóló cikkben volt egy kód, ahol az objektum methodját hívtuk meg. Emlékeztetőül a releváns rész:

var myFirstObject = {
    welcomeText: "Hello, World!",
    greet: function() {
        console.log(this.welcomeText);
    }
}

myFirstObject.greet();

Az objektumok property-jeit elérhetjük a fenti módon (dot notation) és egy másik, eddig be nem mutatott módon is:

myFirstObject["greet"]()

Ezt a formát bracket notation-ként emlegetjük. Ha észrevetted a hasonlóságot a tömb elemeinek eléréséhez használt szintaktikával, akkor jár a mezei pirospont. Az object[key] forma látható itt is, ahol az object a tömb, a kulcs (key) pedig az index ami egy szám. Fontos megjegyezni, hogy bár a szintaktika hasonló, tömbök esetében nem tudunk dot notation formát használni (tehát a myArray.0 szintaktikai hibát eredményez) és pl. a myFirstObject-nél nem tudjuk a property-ket indexeléssel elérni, hiába vannak sorrendben elhelyezve (persze hozhatunk létre property-t ami szám, de ha lehet ne szivassuk magunkat ilyennel):

console.log(myFirstObject[0]) // undefined

Jellemzően a dot notation formát fogjuk használni, de a későbbiekben, kihasználva a nyelv rugalmasságát, fogunk játszani picit a bracket notation-el is.

A tömbünk tartalmazhat akár egy másik tömböt is (jagged array, nested array a két általánosan használt szakkifejezés rá, ugyanakkor multidimensional array-ként is találkozhatsz vele, ekkor azonban jusson eszedbe, hogy ez utóbbi némileg mást jelenthet más nyelveknél), ekkor annak a tömbnek az elemét a következőképpen érhetjük el:

var myArray = ["Alma", "Körte", "Eper"]
var something

var jaggedArray = [
    ['Alma', 'Körte', 'Eper'], // 0-s index
    ['Ford', 'Mondeo', 1992], // 1-es index
    [42, 'Douglas Adams', myArray], // 2-es index
    something
]

console.log(jaggedArray[0][0]) // Alma
console.log(jaggedArray[1][2]) // 1992
console.log(jaggedArray[2][2][0]) // Alma

A tömbünk tartalmaz 3 másik tömböt, sőt a 3. tömb szintén tartalmaz mégegy tömböt. A kódból látszik, hogy hogyan tudjuk az egyes elemeket elérni. Ugyanakkor az is szembetűnő, hogy amennyiben nem konzisztens a tömbök struktúrája, akkor saját magunk számára nehezítjük meg az adatfeldolgozást, hiszen pl. a második tömb 3. eleme nem tömb, tehát ott tovább indexelni nincs értelme, mint ahogy a tömbünk 5. elemét (4-es index, mivel nullával kezdünk) is hiába keressük:

console.log(jaggedArray[1][2][0]) // undefined
console.log(jaggedArray[4]) // undefined

Szerencsére nem tudjuk "túlcímezni" a tömbjeinket (ellentétben a fix méretű tömbökkel, ahol ez hibát eredményez), egyszerűen undefined lesz amit visszakapunk.

Fontos: az, hogy egy elem elérésekor undefined-et kapunk vissza, az nem feltétlenül azt jelenti, hogy "túlcímeztük" a tömböt és nincs ilyen elem. A fenti példába direkt raktam bele egy deklarált (létező változó) ám nem definiált változót, melyre ugyanúgy undefined-et fogunk kapni, ám valójában része a tömbnek.

Tehát a tömb egy speciális objektum. Mivel a tömb "csak egy speciális objektum" ezért vannak metódusai és propery-jei, melyeket használni tudunk. Nézzünk néhány fontos metódust:

  • push/pop: két ellentétes művelet, az első hozzáad a tömbhöz egy új elemet, míg a második kiveszi az utolsó elemet (nem csak az értékét adja vissza, hanem el is távolítja a tömbből)
  • unshift/shift: szintén két ellentétes művelet, és pont azt csinálja mint a push/pop csak a tömb elejével nem pedig a végével. Tehát az unshift hozzáad egy elemet a tömb elejéhez, míg a shift kiveszi az első elemet.

Ezzel a négy metódussal két általánosan használt collection típust tudunk megvalósítani, a:

  • queue: first-in-first-out (FIFO) adatstruktúra, melyet úgy kell elképzelni, mint pl. egy ügyfél várakoztató rendszert, ahol amikor bemész kapsz egy sorszámot (az előtted lévő száma utánit) és a sorszám alapján szólítanak. Ezzel biztosítva van, hogy aki elsőnek érkezett az fog elsőnek sorra kerülni (tulajdonképpen a sorbanállás szabadabb formája, nem kell sorban állnod mégis tudod mikor fogsz sorrakerülni). A fenti metódusokra vetítve ez úgy néz ki, hogy ahogy érkeznek az adatok azok push-al bekerülnek a collection-be, majd amikor fel akarjuk dolgozni, akkor shift-el ki vannak véve a lista elejéről.
  • stack: last-in-first-out (LIFO) adatstruktúra, talán a fentiek alapján egyértelmű, hogy miről is van szó. Megvalósítani a push és pop metódusokkal lehet.

Mivel a push és a pop a collection minden elemét érintetlenül hagyja (hiszen a tömb végéhez kötödik a tevékenységük) ezért gyorsak. A shift és unshift azonban a tömb elejét "piszkálja", ezért a tömb összes többi elemére hatással van, hiszen a művelet előtti sorszámuk különbözni fog a művelet utáni sorszámuktól (át kell sorszámozni őket: hogy valójában mi történik a memóriában az annyira nem érdekes, a lényeg, hogy némi plusz műveletet kell végezni a push/pop-hoz képest) ezért ezek kissé lassabban futnak le. A sorfolytonos indexelés egy lényeges eleme a tömböknek és a nyelv építve erre a sorfolytonosságra optimalizál is ennek kapcsán, ám mivel a tömb "csak egy speciális objektum" ezért használhatjuk az objektumok által kínált lehetőségeket is rajta, pl. rendelhetünk hozzá saját property-t:

var myArray = ["Alma", "Körte", "Eper"]
myArray.theAnswer = 42

Ebben az esetben a tömbünk elkezd "hagyományos" objektumként viselkedni és minden tömbbel kapcsolatos optimalizálást kikapcsol a nyelv. Érdemes azt is észben tartani, hogy a tömb elemeit felül is tudjuk írni, sőt olyan elemnek is tudunk értéket adni ami még nem létezik:

var myArray = [0, 1, 2]
myArray[0] = 42
myArray[10] = 42

console.log(myArray[0]) // 42
console.log(myArray) // [ 42, 1, 2, <7 empty items>, 42 ]

A fenti példán látszik, hogy a nyelv próbálja nekünk megtartani a sorfolytonosságot és beszúrt nekünk 7 üres elemet a tömbbe. A fenti megoldás alapvetően hibás használata a tömböknek, mindenképpen kerülendő.

Bár sokat lehetne még a tömbökről beszélni, már csak egy dolgot említek meg mielőtt lezárom ezt a bejegyzést, ez pedig a tömbök egyik lényeges property-je a: length. Ahogy a nevéből következtethetünk rá, a tömbben lévő elelemk számáról ad tájékoztatást. Ez a property minden alkalommal frissül amikor a tömb módosul, és valójában a legnagyobb indexnél egyel nagyobb az értéke. Érdekes dolog a length-el kapcsolatban, hogy nem csak olvasható, hanem írható is. Ha a jelenlegi értékénél nagyobbra állítjuk, akkor semmi extra nem történik, lesz pár undefined értékünk. Azonban ha a jelenlegi értékénél kisebbre állítjuk, akkor a tömbünket "megcsonkítjuk":

var arr = [1, 2, 3, 4, 5];

arr.length = 2; // 2 elemu lesz a tomb
console.log(arr); // [1, 2]

arr.length = 5; // visszaallitjuk 5 elemure
console.log(arr[3]); // undefined; az elozo ertekek nem jonnek vissza, elvesztek

Álmos szerint el kéne szórnom némi morzsát a többi programozási nyelvből is, hogy ne ragadjunk le az 1 bites user szintjén aki csak a JS-t ismeri, illetve, hogy véletlenül se maradjon bennünk az a tévhit, hogy a fentebb leírtak minden nyelvre igazak lennének. Szóval jöjjön néhány pont arról, hogy ha más nyelvekre kalandoznál mire kell figyelned:

  • a tömbök olyannyira gyakran használt elemei a programozásnak, hogy egyes nyelvek megtisztelték azzal, hogy külön adattípusként kezelik, azaz nem minden nyelvnél "csak egy speciális objektum".
  • hasonló a helyzet a queue/stack esetén is, "ők" is elég gyakoriak (ezért is lettek megemlítve) és pont ezért bizonyos nyelvekben erre már kész megoldások vannak ezen a néven (vagy típusként vagy osztályként) és nem neked kell tömb metódusokkal megvalósítani.
  • fentebb úgy fogalmaztam, hogy "lista szerű adatszerkezet". Ez a kissé ködös megfogalmazás nem véletlenül született meg olyan formán, hanem azért, mert egyes nyelvekben létezik az ún. lista (list) ami a tömbhöz hasonló adatszerkezet több-kevesebb eltéréssel. Ha tehát más nyelvekkel ismerkedsz, akkor jusson eszedbe ez a bekezdés és vizsgáld meg, hogy van-e lista, és ha van, akkor miben más mint a tömb.
  • az erősen típusos nyelveknél a változóknak van ún. default (alap) értéke, pl. integer esetén 0, boolean esetén false, stb. Mivel JS esetében nem tudjuk megadni a változónk típusát, így marad nekünk az undefined. Egyes nyelvek kihasználva az alapértéket, a fix méretű tömbök definiálásakor inicializálják is nekünk a tömböt, feltöltve azt a típusnak megfelelő alapértékkel.

Álmos javaslatai között volt még az "off by one" hiba, amiről inkább a következő cikkben írnék, ahol a feltételvizsgálatról és a ciklusokról lesz szó.


Mit tanultál ebben a leckében?

  • megismerted az egyik legfontosabb beépített objektum típust a tömböt
  • megismertél négy fontos metódust és egy fontos property-t a tömbökkel kapcsolatban
  • megtanultad, hogy ha figyelmetlenül használod a tömböket, akkor annak teljesítménycsökkenés és/vagy extra memória használat az ára