17 May

Javascript O(O)P


A webfejlesztésnek, akár akarjuk, akár nem, bizony szerves részét képezi a frontend, így az életutunkbizony keresztezni fogja némi javascript, kivéve, ha dedikáltan backendesek vagyunk.dHCqj


Viszont amit a javascript terén teszünk, az az esetek túlnyomó többségében nem nevezhető fejlesztésnek, inkább csak különböző library-ket rántunk be, esetleg jquery selectorokat hívogatunk meg és próbaljuk minimálisra csökkenteni azt az időt, amíg az IDE-ben JS kód van megnyitva.
Ha a kódunk tényleg csak a globális névtérbe dobált változók tömkelege, akkor sajnos a helyzet nem is fog változni, főleg akkor ha Php-ben megszoktuk a jól struktúrált/névterekre bontott kódot.


Pedig a javascript is tud ám ilyet! (a maga módján)


Tegyük fel, hogy nem szeretnénk a globális névteret teleszemetelni, így névtereket akarunk definiálni:
Mi sem egyszerűbb?  Javascriptben az egész annyi, hogy létrehozzuk a hozzá tartozó objektumot:



var MyNamespace = {};

Bumm, kész is van a névterünk! Viszont ezzel túl sokra nem megyünk, nem árt ha létrehozunk az adott névtéren belül valami osztályt (ami elég morbid lesz, mivel a javascript-ben nincs class keyword, hanem az osztályokat, mint function-öket fogjuk definiálni )



MyNamespace.Translate = function(locale){
    this.locale = locale; // ezt felfoghatjuk az osztályunk konstruktorának

}

A fenti osztály viszont példányosítás nélkül mit sem ér, tehát akkor hozzuk létre:



var trans = new MyNamespace.Translate("hu_HU"); // példányosítottuk és átadtuk neki a locale értékét, aztán letároltuk az osztályt a trans változóba


console.log(trans.locale); // "hu_HU" 



Na de akkor most jöjjön az ami miatt a legtöbben felhagynak a komplexebb javascript kódok írásával, a macera. Hozzunk létre egy függvényt az adott osztály alatt!



MyNamespace.Translate.trans = function(key) {
 // ide jön valami roppant frappáns fordítási logika

 return value;
}

var trans = new MyNamespace.Translate("hu_HU");
console.log(trans.trans(key)); // ide jönne a value, ugye?

Na most amikor a fenti kódot lefuttatjuk, akkor bizony szomorúan kell tapasztaljuk, hogy a programunk bizony csecsre hibára fut. A gond ott van, hogy a javascript másképp kezeli az objektumokat, mint azt pl. Javaban, PHP-ben megszoktuk. A fent leírt Mynamespace.Translate.trans() egy statikus metódusnak minősül, tehát amikor egy példányon akarjuk meghívni, akkor bizony nem találja a rendszer. Ahhoz, hogy ne statikus legyen és ezen a példányon megtalálja, ahhoz az adott objektum ún. prototype-ját kell megb*szatnunk piszkáljuk.


Minden javascriptben példányosított objektum a példányosításkor megnézi a saját prototype-ját és az esetleges általunk beregisztrált módosításokat figyelembe véve fogja példányosítani.


Tehát a megoldás a következő lesz:



MyNamespace.Translate.prototype.trans = function(key) { // a prototype objektumba elhelyezzük a mi kis bónuszunkat, így a példányosított osztályban már ott lesz a trans() metódus. Ez igaz a this context-ben meghívottakra is.

 // ide jön valami roppant frappáns fordítási logika

 return value;
}

Ezzel ugye nem csak az egyes metódusait, de példányváltozókat is beregisztrálhatunk, értéket rendelhetünk hozzá, stb. Nem a legelegánsabb megoldás, de jelen körülmények közt ezzel kell beérjük.



This == ?


Az iménti kódból kimaradt az a jóféle fordítási logika amit itt orvosolni fogunk. Az adott fordítási kulcs/ érték párok egy tömbben lesznek eltárolva, amin mi egy jól bevált forEach-el szimplán menjünk végig keresve az "igazit".



MyNamespace.Translate.prototype.trans = function(key) {

Mynamespace.Translate.keys.forEach(function(elem){ // végigiterálunk egy objektumokkal teli tömbön

 if(elem.key == key) // ha megvan az elem

 this.value = elem.value; 
 });

}

Na most a fenti kód nekünk nem lesz jó, ugyanis a callback function scopejában létrehozott változó nem fog megjelenni a return-ben, ugyanis az nem a mi osztályunk this-ére vonatkozik, hanem a z adott tömbre.
Ilyen esetekben létrehozhatunk egy változót, ami az osztálypéldányunkra mutat és a foreach callbacken belül használhatjuk ezt.



MyNamespace.Translate.prototype.trans = function(key) {

var self = this; // az osztályunk referenciáját egy globális változóba tesszük


Mynamespace.Translate.keys.forEach(function(elem){ // végigiterálunk egy objektumokkal teli tömbön

 if(elem.key == key) // ha megvan az elem

 self.value = elem.value;  // a self mivel átkerült a globális scope-ba, ezért itt is elérjük és rajta keresztül a Translate osztályunknak tudunk példányváltozót beállítani

 });

}

Öröklődés


Aki elég beteg, az eljutott eddig a cikkben, annak már valószínűleg nem fogja megülni a gyomrát amikor azt mondom, igen, a javascriptben is lehet ilyet, persze a már jól megszokott sajtreszelővel r*jszolós módszerrel.

Az alábbi példában létrehozok egy kaja osztályt, aztán abból leszármaztatok egy gyros osztályt (nem, nem vagyok éhes).
var kaja = function(kaloria) { 
   this.kaloria = kaloria; // ez itt a konstruktorunk ugye

}

var kaja.prototype.zaba = function() {
   console.log("Épp most zabáltál fel " + this.kaloria + " kalóriának megfelelő kaját!");
   // itt ugye a kaja alatt hozunk létre egy metódust, tehát elérjük a kaja osztályunk this context-jét

}

var gyros = function(pitaban) {

    kaja.call(this, 500 ) // a call metódussal tudjuk meghívni a "szülő" konstruktorát, amolyan parent::__construct(). Mivel a hívás során átadjuk az objektumunk referenciáját, ezért a this context-ben fog meghívódni. 

    this.pitaban = pitaban; // a boolean pitában kérdés, amit olyan gyakran hallunk

}

// ez a tényleges öröklődés, ahol a kaja prototípusát megörökli a gyros osztályunk.

gyros.prototype = Object.create(kaja.prototype); 

// mivel a kaja osztályunknak is van constructor-a, ezért ezt vissza kell állítsuk, mielőtt példányosítanánk, mert az imént ugye felülírtuk

gyros.prototype.constructor = gyros;


var jofeleGyros = new gyros(true); // példányosítjuk a gyros osztályt


// majd leellenőrízzük, hogy tényleg végbement-e az öröklőgés

console.log(jofeleGyros instanceof gyros); // true

console.log(jofeleGyros instanceof kaja); // true

// és ellenőrízzük, hogy az örökölt metódust is megkaptuk-e

jofeleGyros.zaba(); // Épp most zabáltál fel 500 kalóriának megfelelő kaját!

Ha a prototype kulcsszótól már herótunk van és nem akarjuk hosszan definiálgatni a dolgot, megoldhatjuk ezt úgy is, hogy minden alkalommal prototype objektumokat adunk át:
gyros.prototype = {
 constructor: gyros, // átadjuk ugye a constructorunkat, nehogy elvesszen

 zaba : function(kaloria) { // mennyivel jobb, szárazabb érzés, ugye?

 } 
}

Első körben ennyit szerettem volna kitárgyalni a JS-ről azoknak, akik még nem nagyon mélyedtek el benne, legközelebb végignézzük az egyes tervezési mintákat itt is.

 

Hozzászólások betöltése
2014-2018 © Letscode.hu. Minden jog fenntartva. Build verzió: 1.2.12