A gyakorlat anyaga¶
Ezen a gyakorlaton megismerkedünk a JavaScript nyelven történő objektumorientált programozással.
Osztályok, objektumok¶
A korábban megismert objektumorientált paradigma alapvető elemei az osztályok. Ezek tulajdonképpen azonos tulajdonságokkal és viselkedéssel rendelkező objektumok általánosításai, formai leírásai.
Érdekes módon a JavaScriptben nem mindig voltak osztályok. Az osztály, mint beépített nyelvi elem csupán a 2015-ös ECMAScript6 (ES6) szabványban jelent meg. Arról, hogy az osztályok bevezetése előtt milyen megoldások voltak JavaScriptben az objektumok általános formai leírásának elkészítésre, az előadáson lesz szó.
Osztályok létrehozása¶
A fentebb említett ECMAScript6 szabvány bevezette a class
kulcsszót, aminek segítségével osztályokat hozhatunk létre JavaScriptben.
Példa: Egy Vehicle
nevű osztály létrehozása
1 2 3 |
|
Adattagok, metódusok¶
Egy osztálynak lehetnek adattagjai, illetve metódusai (tagfüggvényei). Ezekre JavaScriptben a .
(pont) operátorral tudunk hivatkozni.
Az osztály metódusait a függvényekhez hasonló módon hozhatjuk létre, azzal a fontos különbséggel, hogy a függvények definiálásakor használt function
kulcsszót elhagyjuk.
Példa: Egy paraméter nélküli info()
és egy egyparaméteres boardPassenger()
metódus.
1 2 3 4 5 6 7 8 9 |
|
Pythonhoz hasonlóan, JavaScriptben sem deklaráljuk az adattagokat külön az osztályban. Az adattagok létrehozása és inicializálása itt is a konstruktorban történik.
Konstruktor¶
JavaScriptben a constructor()
névre hallgató speciális metódus lesz az osztály konstruktora. Ez természetesen akkor kerül meghívásra, amikor az osztályt példányosítjuk. A konstruktort használjuk az osztály adattagjainak létrehozására és inicializálására.
Amennyiben az osztályunkba nem írunk konstruktort, akkor az interpreter automatikusan gyártani fog egy paraméter nélküli konstruktort (default konstruktort), ami az osztály példányosításakor fog meghívódni.
JavaScriptben a this
kulcsszóval hivatkozhatunk az aktuális objektumra. Ha egy osztályon belül hivatkozni szeretnénk egy adattagra vagy egy metódusra, akkor a kérdéses adattag vagy metódus neve elé mindig kötelezően ki kell írni a this
kulcsszót!
Példa: Egy két paraméteres konstruktor, amely paraméterei alapján inicializáljuk a brand
és speed
adattagokat.
1 2 3 4 5 6 |
|
JavaScriptben továbbra sincs function overload. Ha azt szeretnénk elérni, hogy egy metódust többféle, eltérő paraméterezéssel is tudjunk használni, akkor használjuk a default függvényparamétereket!
Példa: Írjuk át a Vehicle osztály konstruktorát úgy, hogy a speed paraméter értékét ne legyen kötelező megadni, alapértéke legyen 0!
1 2 3 4 5 6 |
|
Példa: Egészítsük ki az osztályunkat a következőkkel:
- A konstruktorban hozzunk létre egy
passengers
adattagot, amit egy üres tömbbel inicializáljunk! - Írjuk meg a
boardPassenger()
metódust, amely egy utas nevét (szöveges adat) várja paraméterül! A metódus szúrja be a paraméterül kapott utas nevét apassengers
tömb végére! - Készítsük el az
info()
metódust, ami kiírja a jármű márkáját, sebességét és az utasok számát!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Class fields (modern módszer)¶
Modern JavaScriptben közvetlenül az osztály törzsében is deklarálhatunk adattagokat, nem csak a konstruktorban:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Előnyök: - Egyértelműbb, hogy az osztálynak milyen adattagjai vannak - Nem kell minden adattagot a konstruktorban inicializálni - Jobb olvashatóság
Objektumok létrehozása¶
A példányosítás során az osztályból egy objektumpéldányt hozunk létre.
Az osztály példányosítása, avagy egy új objektum létrehozása JavaScriptben a new
kulcsszó segítségével történik. A példányosításkor meghívjuk az osztály konstruktorát, és átadjuk neki a megfelelő paramétereket (ha vannak).
Példa: A Vehicle osztály példányosítása, metódusok meghívása
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Kimenet
Skoda brand vehicle, with speed 90 km/h, carrying 1 passengers.
Ikarus brand vehicle, with speed 0 km/h, carrying 0 passengers.
Láthatóságok, getterek, setterek¶
A Pythonhoz hasonló módon JavaScriptben sem tudtuk szabályozni módosítószavakkal az adattagok láthatóságát (vesd össze: Java). Alapértelmezett módon minden adattag publikus, azaz mindenhonnan elérhető.
Konvenció alapján az adattag neve előtti egyszeres alulvonás jelzi azt, hogy az adattag nem publikus használatra van szánva ("private adattag"). Viszont ettől az adattag kívülről továbbra is elérhető lesz!
Példa: Jelezzük, hogy a márka és sebesség adattagokat nem publikus használatra szánjuk!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Private fields (modern megoldás)¶
A modern JavaScriptben (ES2022+) valódi privát mezőket hozhatunk létre a #
szimbólum használatával:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Ez azonban csak ES2022+ böngészőkben és interpreterekben működik. Ebből kifolyólag, ha ezt használjuk, kompabilitási gondok léphetnek fel!
Getterek és setterek¶
Ha a nem publikus használatra szánt adattagok szabályozott elérését és módosítását szeretnénk elérni JavaScriptben, akkor készíthetünk ezekhez az adattagokhoz gettereket, illetve settereket.
JavaScriptben a gettert, valamint a settert property-ként valósíthatjuk meg. A get property-t a get
, míg a set property-t a set
kulcsszóval hozhatjuk létre. A property-k használatával szabályozott módon kérhetjük le és állíthatjuk be adattagok értékét úgy, hogy kívülről azt a látszatot keltjük, mintha új, publikus adattagokkal dolgoznánk.
Példa: Készítsünk gettert és settert az _speed
adattaghoz!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
Kimenet
Speed value can only be a non-negative number!
60
Figyelem
Fontos, hogy a property és az adattag neve mindig eltérő legyen, különben végtelen rekurzióba futunk! A példában a property neve speed
(alulvonás nélkül), az adattag neve ettől eltérő módon _speed
(alulvonással).
1 2 3 4 5 6 7 8 9 |
|
Statikus metódusok és property-k¶
A statikus metódusok és property-k az osztályhoz tartoznak, nem az egyes példányokhoz. Ezeket a static
kulcsszóval hozhatjuk létre.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Persze ehhez hasonló osztály létezik, így nem szükséges ezt elkészítenünk.
Gyakorlati példa a Vehicle osztállyal:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
Öröklődés¶
A korábbi tanulmányainkból ismerős lehet számunkra az öröklődés fogalma. Ez egy osztályok közötti kapcsolat, amely egy úgynevezett ősosztály (szülőosztály) és egy gyermekosztály (leszármazott osztály) között valósul meg. A gyermekosztály tulajdonképpen az ősosztályának egy speciális változata lesz (pl. a Dog
osztály az Animal
osztály gyermeke, hiszen minden kutya egyben állat is).
Az öröklődés során a gyermekosztály megörökli az ősosztályának összes adattagját és metódusát. A gyermekosztályban létrehozhatunk az örökölt adattagokon kívül még egyéb adattagokat, metódusokat, illetve lehetőségünk van az örökölt metódusok működésének felüldefiniálására is (overriding).
JavaScriptben az öröklődést a következő szintaxissal adhatjuk meg:
1 2 3 |
|
Fontos megjegyezni, hogy JavaScriptben csak egyszeres öröklődés van, tehát egy osztálynak nem lehet kettő vagy több ősosztálya.
A gyermekosztályból hivatkozhatunk az ősosztályra, annak adattagjaira és metódusaira a super
kulcsszóval. Ennek segítségével meghívhatjuk az ősosztály konstruktorát az adott osztályon belül. Ha a gyermekosztályban nem hozunk létre új adattagot, akkor az ősosztály konstruktorának meghívása elhagyható.
Példa: Hozzunk létre egy Car
osztályt, ami az imént elkészített Vehicle
osztályból származik!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
|
Kimenet
BEEP BEEP!
Trabant car, (speed: 40 km/h), passengers: 0, self-driving: no
Tesla car, (speed: 130 km/h), passengers: 2, self-driving: yes
Super kulcsszó használata metódusokban¶
A super
kulcsszóval nem csak a konstruktort hívhatjuk meg, hanem az ősosztály bármely metódusát:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
Method chaining (fluent interface)¶
A this
visszaadásával lehetővé tesszük a metódushívások láncolását:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
Típusellenőrzés¶
Amint azt korábban láthattuk, a typeof
operátorral le tudjuk kérni, hogy egy adott objektum adott típusú-e.
1 2 |
|
Fontos megjegyezni, hogy a typeof
csak beépített típusokra működik! Ha egy saját osztályból példányosított objektumról szeretnénk eldönteni, hogy adott típusú-e, akkor az instanceof
operátor használatos. Az obj instanceof ClassName
visszaadja, hogy az obj
objektum ClassName
osztály példánya-e.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|