ugrás a tartalomhoz

ECMAScript 5 – osztályok már(pedig) léteznek

presidento · 2011. Dec. 31. (Szo), 15.35

Alábbiakban a júliusi Budapest.js meetupon előadott témámat igyekszem írásban kifejteni. Az foglalkoztatott, hogy hogyan lehetne az objektumorientáltságot jól megvalósítani JavaScript alatt.

Objektumorientáltság

A JavaScript objektumorientált, de nem osztály, hanem prototípus alapú. A kettő közötti lényeges különbséget valahogy úgy tudnám szemléltetni, hogy az osztály egy absztrakt valami, ami konkrét példányban kezd el élni. Mint egy pontosan meghatározott tervrajz, melyet megvalósítva lesz egy autónk. A prototípusos objektumorientáltság ezzel szemben mintapéldányokkal dolgozik: például van egy konkrét, létező cipőnk, elvisszük a cipészhez, hogy szeretnénk egy ilyet, csak legyen magasabb a sarka, és legyen több dísz az oldalán. A JavaScript szemléletét nehéz megérteni, ennek egyik oka, hogy keverve használja az elnevezéseket. Erre nem térnék ki részletesebben, korábban írtam róla.

A this működése

Jó cikkek születtek a this-ről, csak a lényeget foglalnám össze: általánosságban véve egy függvény futásakor a this kulcsszó értéke a függvényhívás előtt álló objektum. Fontos azt látni, hogy a JavaScriptben a függvény külön életet él. Az, hogy neve van, vagy éppen egy objektumhoz tartozik, semmit nem jelent, a kérdés, hogy hogyan hívtuk meg. A következő kódrészletben az alsó blokkban mindenhol ugyanazt a függvényt hívjuk meg, különböző this értékekkel, ezek vannak a megjegyzésben.

function a() {};
x.b = a;
y.z.q = x.b;

a(); // undefined/global scope
x.b(); // x
y.z.q(); // y.z

Mivel a függvények ilyen könnyen pakolhatóak egyik objektumból a másikba, nagyfokú rugalmasság érhető el. A MicroEvent.js jó példa arra, hogy nem kell származtatni, ha egy gyakran használt funkcionalitással szeretnénk felruházni egy objektumot. Csupán elkészítjük a függvényeket és változókat, és kiterjesztjük vele az eredeti objektumunkat. (Ez a mixin vagy trait programozási paradigma.) Egy egyszerűbb példában egy számlálót valósítunk meg:

var COUNTER = {
    _counter: 0,
    
    getNext: function () {
        return ++this._counter;
    }
};

var myObject = {
    number: 42
};

myObject.getNext = COUNTER.getNext;
myObject._counter = COUNTER._counter;

var myObject2 = {
    string: 'HELLO'
};

myObject2.getNext = COUNTER.getNext;
myObject2._counter = COUNTER._counter;

Hogyan került a homokszem a gépezetbe?

Ez mind szép és jó lenne, de az a gond, hogy a JavaScriptben csak egyetlen függvénytípus létezik, ez viszont nagyon sokféle formában előfordul. Tegyük fel, hogy egy metódusban AJAX kérést kell indítanunk:

object.method = function () {
    this.request({
        parameter: this.counter,
        
        callback: function (response) {
            this.counter += response.data;
        }
    });
};

Nem fog menni. Ugyanis a callback paraméterben megadott valami egy függvény, melynek meghívásakor a this nem valószínű, hogy az object-re fog mutatni, hiszen nem úgy hívtuk meg, hogy object.callback(). De nézhetjük az újonnan bevezetett tömbkezelő függvényeket is. Ha egy tömböt értékeit szeretnénk összegezni egy saját függvény szerint:

object.getSum = function () {
    return this.myArray.reduce(function (previous, current) {
        return previous + this.getValue(current);
    }, 0);
};

És még sorolhatnám a példákat, hiszen van bőven: időtúllépés, egyéb események kezelése.

Mi a megoldás?

Valahogyan különbséget kellene tudnunk tenni a metódusok és az egyéb (kisebb) függvények között. Hogy egy metóduson belül mindig tudjunk hivatkozni az objektumunkra.

_this

Az egyik lehetőség, hogy a függvény futásakor létrehozunk egy változót, ami az aktuális this-re mutat. Ha ezt a módszert választjuk, célszerű lerögzíteni, és minden függvény elején ezt bevezetni, s onnantól csak ezt használni (és nem az eredeti this-t), hiszen így a kódrészleteink áthelyezhetőek maradnak, míg ha keverve használjuk a this-t és a _this-t, akkor át kell írni, ha valami kikerül egy callbackből. Az előző példáknál maradva:

object.method = function () {
    var _this = this;
    
    _this.request({
        parameter: _this.counter,
        
        callback: function (response) {
            _this.counter += response.data;
        }
    });
};

object.getSum = function () {
    var _this = this;
    
    return _this.myArray.reduce(function (previous, current) {
        return previous + _this.getValue(current);
    }, 0);
};

Számomra kissé természetellenesnek tűnt, hogy mindig külön meg kell adni az objektumot, de ez is megszokható.

Új nyelvi elem függvény létrehozására

A JavaScriptben a hatókör függvényszintű, és nagyon gyakran kell kiírnunk azt a jó hosszú szót, hogy function. Ez másoknak sem tetszik, és az eddig tárgyalt problémákkal együtt felmerült, hogy biztosítani kellene a lehetőséget arra, hogy röviden hozzunk létre kisebb függvényeket, és ha ezt megtehetjük, akkor ezek a rövid függvények ne módosítsák a this értékét. A ECMAScript wiki oldalán a következő javaslat szerepel:

object.method = function () {
    this.request({
        parameter: this.counter,
        
        callback: #(response) {
            this.counter += response.data;
        }
    });
};

object.getSum = function () {
    return this.myArray.reduce(#(previous, current) {
        prev + this.getValue(current);
    }, 0);
};

Itt hashmarkkal lehet megadni a rövidített függvényt, és nem kell kiírni a return-t sem. (Ellenvéleményként szerepel, hogy Brendan Eich szintén szimpatizált a hashmarkkal, de ő konstans objektumok létrehozására szerette volna.) Ez még amúgy is csak javaslat, és valószínűleg nem fogják elfogadni.

A SafeJS hasonló módon terjeszti ki a JavaScript lehetőségeit. Itt a @ jel a „this.”-nak a rövidítése, a # ugyanúgy a függvény rövidítése, a return pedig „->”-ként írható. Itt a #@ kifejezéssel létrehozott függvények őrzik meg a this értékét.

object.method = function () {
    this.request({
        parameter: this.counter,
        
        callback: #@(response) {
            @counter += response.data;
        }
    });
};

object.getSum = function () {
    return this.myArray.reduce(#@(previous, current) {
        -> previous + @getValue(current);
    }, 0);
};

Nagyon-nagyon tetszik, amit a SafeJS szeretne megvalósítani, de biztos, hogy nem fogom használni. Abban a szerencsés helyzetben vagyok, hogy munkahelyemen nem kell a böngészőkompatibilitással bajlódni. Lévén, hogy a JavaScript a Mozillánál (pontosabban a Netscape-nél) született és élte korai fejlődését, ők is biztosítanak olyan dolgokat, amik jó lenne, ha benne lennének a mainstreamben. Gondolok itt a let, const kulcsszóra, az E4X-re sít. Önként mondtunk le ezeknek a használatáról (pedig mint ifjú titán én is igyekeztem volna bevezetni), hiszen onnantól kezdve se JSHint, se JSBeautifier.

Ha nem lehet _this, akkor legyen that!

De nevezhetném self-nek, vagy THIS-nek is, attól függ, ki honnan jött. A fenti problémákkal szembesülve ötlik fel a lelkes JavaScript programozóban a lexikai zárvány használatának a lehetősége:

function Constructor() {
    var that = this;
    
    /* … */
    
    that.method = function () {
        that.request({
            parameter: that.counter,
            
            callback: function (response) {
                that.counter += response.data;
            }
        });
    };
    
    that.getSum = function () {
        return that.myArray.reduce(function (previous, current) {
            return previous + that.getValue(current);
        }, 0);
    };
    
    return that;
};

var object = new Constructor();

Így az „osztályon” belül a that mindig az aktuális objektumra mutat. Ráadásul így egyszerűen tudunk objektumhoz kapcsolt metódust átadni:

setTimeout(object.method, 1000);

// és nem kell:

setTimeout(function () {
    object.method();
}, 1000);

Ennek is megvan azonban a hátránya: jól tudunk így önálló objektumokat készíteni, lehetnek privát változóink a zárványon belül, de az öröklődés nehezebben megoldott. Kalandra fel, új ismeretünket felhasználva evezzünk kissé mélyebb vizekre!

Osztály vagy nem osztály?

A JavaScriptben nincs osztály, mint nyelvi elem, és nem is szabad így gondolni a konstruktor függvényre. Különben is, amit meg tudunk valósítani, az nagyon messze áll az osztály fogalmától, legalábbis amit én osztálynak tartok. Számomra egy osztálynak nevezett valami a következő tulajdonságokkal rendelkezik:

  • Vannak „metódusai” (függvények) és „adattagjai” (változók).
  • Van lehetőség ezek láthatóságának a meghatározására (legalább private, protected, public szinten).
  • Van definíciós része és működési része. A két kód lehet ugyanott, akár vegyesen, de pontosan meg van határozva a struktúra, és az nem változik a futás közben. Borzalmas, amikor egy 1400 soros objektum 577. sorában érték adódik valami addig nem létező objektum szintű változónak, amit valahol fogunk használni, de igazából nem tudjuk, ki hivatkozik rá, még az is lehet, hogy kívülről is el akarják érni… Az osztály definíciója legyen egyértelmű. Lássam, milyen funkciókat valósít meg, ehhez milyen belső mechanizmust használ, és ne legyenek egyszercsak megjelenő mágikus dolgok benne.
  • A láthatóság meghatározása a definíciós lépésben történik, utána nem kell foglalkozni vele. Ha valami protected, akkor egyszer mondjam meg róla, hogy protected, ne kelljen mindig kitennem elé az aláhúzásjelet. Veletek nem fordult elő, hogy valamilyen korábban privátnak szánt funkciót igényelt az „ügyfél”, és biztosítani kellett a számára? A klasszikus JavaScriptben jelölhetjük a saját dolgokat aláhúzással, ilyen esetben sok helyen át kell írni – vagy nem foglalkozunk vele, és minden aláhúzás nélküli, de akkor nem fogunk tudni kiigazodni a saját kódunkon, nem tudjuk, mibe nyúlhatunk bele, mi az, ami az „API-hoz” tartozik, mit lehet bátran refaktorálni.
  • Lehessen osztályok közötti hierarchiát, öröklődést megvalósítani.
  • Leszármazott osztályban megváltoztathatom valaminek a láthatóságát (például protected-ről public-ra) úgy, hogy az ős osztályt nem kell módosítanom.
  • Az osztály metódusai „kötve” vannak az osztályhoz, nem élnek külön életet. Az object.f ne egy függvény legyen, hanem az object f() függvénye. Akárhol, akármilyen formában hívom meg.
  • A metódusokat leszármazott osztályban felül lehet definiálni (de úgy, hogy szükség esetén elérhető az ősosztály adott metódusa), az adattagoknak pedig értéket tudok állítani.
  • Az osztály példányai ellenőrizhetőek instanceOf jellegű vizsgálattal (a leszármazott osztály példányai az ősosztálynak is példányai).
  • Lehessen konstruktort megadni, amely az adatok inicializálását végzi. Ha a leszármazott osztályban nincs megadva konstruktor, a szülő konstruktora automatikusan hívódjon meg, egyéb esetben kézzel. (A JavaScriptben gondot okoz, ha a szülő konstruktora paramétert vár).

ECMAScript 5 alatt ezek a feltételek teljesíthetőek!

Készítettem egy proof of concept implementációt, amely teljesíti a fenti elvárásokat. A forráskód a GitHubon elérhető, a fentebb említett követelmények tesztként részletesen ki vannak fejtve, a szintakszis miatt néhány részletet kiemelek. Úgy gondolom, nem szükséges bőven kifejtett magyarázat, hiszen pontosan úgy működik, ahogy azt például Javaban megszoktuk.

function SuperClass() {
    var that = ES5Class.create(this);
    
    // Definíciós rész, itt kell megadni a láthatóságot,
    // utána már csak annyi kell, hogy pl. that.privateVariable
    
    that.private.privateVariable     = 'super private';
    that.protected.protectedVariable = 'super protected';
    that.public.publicVariable       = 'super public';
    
    that.private.privateFunction     = function () {return 'super private';};
    that.protected.protectedFunction = function () {return 'super protected';};
    that.public.publicFunction       = function () {return 'super public';};
    
    return ES5Class.finalize(that);
}

function SubClass() {
    var that = ES5Class.create(this, SuperClass);
    return ES5Class.finalize(that);
}

var object = new SubClass();

function MyClass() {
    var that = ES5Class.create(this, arguments);
    
    that.protected.v  = 2;
    that.protected.f  = function () {return 2;};
    that.protected.fn = null;
    
    that.__constructor__ = function (variable, f) {
        that.v  = variable;
        that.fn = f;
        
        try {
            // Methods cannot be changed
            that.f = f;
        } catch(e) {}
        
        try {
            // You can not add new properties here
            that.nonExists = 'not work';
        } catch(e) {}
    };
    
    return ES5Class.finalize(that);
}

function MySubClass() {
    var that = ES5Class.create(this, arguments, MyClass);
    
    that.__constructor__ = function (variable) {
        that.super.__constructor__(variable + 1, function () {return 4;});
    };
    
    return ES5Class.finalize(that);
}

var mySubObject = new MySubClass(3);

Összegzés, tapasztalat, vélemény

A kód ECMAScript 5 alatt működik, ezt minden újabb böngésző támogatja. A jól megírt program sikeres futtatásához elegendő, ha van getter és setter támogatás, ilyenkor nincs ellenőrzés a létrehozás utáni változások elkerülésére. (Egyetlen kivétel a Safari volt, ami az object.super kifejezésre fenntartott szó hibát dob.)

A projektet sikeresnek tartom, hiszen a kitűzött cél megvalósításra került – a gyakorlatba azonban mégsem került bele, ugyanis az összes többi függvénytár, eszköz és IDE másfajta gondolkozásmódot követ.

A Budapest.js-en is elhangzott az a kérdés, hogy miért akar mindenki osztályt tenni a JavaScriptbe, hiszen ennek a nyelvnek is megvan a maga logikája, szemlélete. A válasz egyszerű: mert bonyolult JavaScriptben JavaScript gondolkodásmóddal programozni – ezzel kezdtem a bevezetést –, hiszen csak egyetlen függvény nyelvi elemünk van, a this átadogatása meglehetősen nyakatekert módszer. Szerencsére most már van szép megoldás, ez pedig a CoffeeScript, amely arra törekszik, hogy a JavaScript hiányosságain javítson, de a szemléletét megtartsa („The golden rule of CoffeeScript is: »It’s just JavaScript«.”). Leegyszerűsítették a függvények létrehozását, és új nyelvi elemet vezettek be. A -> megegyezik a JavaScript function-nal, a => használata esetén pedig a függvény belsejében ugyanaz marad a this, mint ami kívül volt (mintha a fentebb kifejtett _this módszert használnánk).

 
presidento arcképe
presidento
FARKAS Máté, lelkes fejlesztő, egykori rendszeres előadója a budapest.js meetupoknak. Szereti a kihívást és szeret nála profibbakkal együtt dolgozni.
1

Nagyon jó írás, gratulálok,

virág · 2011. Dec. 31. (Szo), 21.55
Nagyon jó írás, gratulálok, de mégsem tudok veled mindenben egyetérteni, szerintem a JavaScript-nek nem hiányossága, hanem erőssége, tulajdonsága, egyedisége az, amit a cikkben Te is hiányosságnak írtál le: "mert bonyolult JavaScriptben JavaScript gondolkodásmóddal programozni" szerintem ez nem igaz, ez csak szemléletbeli dolog, persze, ettől az a módszer amit leírtál még hasznos és biztosan sokan használják és fogják használni, nekem is tetszik. Csak... :)

+ BÚÉK minden Weblaboros olvasónak, fejlesztőnek! :)
2

Egyetértek. Miért kell

Hidvégi Gábor · 2012. Jan. 1. (V), 15.19
Egyetértek. Miért kell mindenfélét beleerőltetni egy nyelvbe, amit az ember máshol látott? De ha már nagyon azt akarják, akkor legyen osztályalapú, akkor tudjon minden konstrukciót, amit pl. a java, és felejtsük el a prototípusokat, mert annak nincs értelme, hogy egyesével szivárogtassuk be az új funkciókat, meg párhuzamosan lehessen ígyis-úgyis dolgozni benne.

Annak meg nem látom értelmét a mindenféle kódkiegészítők korában, hogy rövidítsük le a függvények nevét #-ra stb.
4

Miért nem lehetne "csak úgy" elhagyni?

zzrek · 2012. Jan. 1. (V), 21.25
Az merült fel bennem, hogy miért nem lehetne "csak úgy" elhagyni a "function" kulcsszót?

object.method = () {  
    this.request({  
        parameter: this.counter,  
          
        callback: function (response) {  
            this.counter += response.data;  
        }  
    });  
}; 
Nem kell # sem, szerintem ez is értelmezhető úgy, hogy az object method-ját definiáljuk (akár úgy, hogy benne pedig az object-et jelenti a "this").

Object literallal is:


obj=
{
 a:2,
 f:(p){return(p+1)}
}

Hogyan másként lehet értelmezni az f utáni kifejezést, mint függvényként?
7

A probléma a parser-nél lehet

inf · 2012. Jan. 2. (H), 05.32
A probléma a parser-nél lehet ezzel kapcsolatban. A zárójel mást is jelöl a javascriptben és ha nincs előtte a function, akkor a feldolgozó nem tudja, hogy melyik jelölésmódról van szó.

    obj=  
    {  
     a:2,  
     f(p):{return(p+1)}  
    }  
Ez talán jobb lenne metódusokhoz, a függvényekhez meg maradhatna az eredeti változat. Én egy típusellenőrzést meg egy polimorfizmust szívesen vennék js-től, hogy könnyebben lehessen újrahasznosítani a kódot, de persze ezt magamtól is meg tudom írni. Igazából ha nem lett volna msie, akkor már csináltam volna saját keretrendszert, viszont annyiszor húzta már át a számításaimat az a böngésző, hogy elegem lett, és abbahagytam a javascriptes gányolást. Nem is szívesen kezdenék neki újra, de ha mégis, akkor fix, hogy valamilyen létező keretrendszerrel, vagy írnék egy szabványkövető kódot, és nem foglalkoznék az ettől eltérő böngészőkkel.
9

Az instanceof operátorral nem

Karvaly84 · 2012. Jan. 2. (H), 07.04
Az instanceof operátorral nem tudunk úgy sem operálni, mert MISE-ben a DOM Node-ok, és a window sem leszármazottja az Object-nek. Én anno írtam egy kis rendszert, amivel meg lehetett valósítani az overloading-ot, de ugye az MSIE-ben nem volt hasznos, úgy hogy kukába raktam :D
10

Hát jah, nekem is volt egy

inf · 2012. Jan. 2. (H), 12.56
Hát jah, nekem is volt egy fél keretrendszerem, az is ment a kukába, amikor odaértem vele, hogy a selectorText-el lekért Node-okat nem lehet HashSet-be tenni, mert nem lehet hashCode-ot beállítani rajtuk... :S
11

Sajnos nem értem

zzrek · 2012. Jan. 2. (H), 13.28
Sajnos nem tudom mire gondolsz, de biztosan igazad van.
( Nekem úgy tűnik, hogy ez maximum valamilyen hierarchia kezeléssel megoldható a parser számára: ha nincs előtte kulcsszó (pl if) akkor a zárójel-kapcsoszárójel kombót értelmezze függvénydeklarációként. )
12

Ez úgy megy, hogy sorba megy

inf · 2012. Jan. 2. (H), 17.31
Ez úgy megy, hogy sorba megy a kódon és felismer mintákat vagy karaktereket.
pl:
function name(arg1, arg2) {blokk}

function -> függvény következik
name -> függvény neve
( -> paraméter lista kezdete
arg1 -> első paraméter neve
, -> következő paraméter jön
arg2 -> második paraméter neve
) -> vége a paraméter listának
{ -> blokk kezdete
blokk -> blokk belseje
} -> blokk vége


Ezek így szépen sorban mennek. Nem tudom pontosan hogyan, lehet, hogy regex-el, lehet csak karaktereket néznek, passz...
Ami biztos, hogy ahhoz, hogy (...){-ot érzékelni tudja a regex, ahhoz positive lookahead szükséges, ami meg annyira nem mindennapos regex feature...
13

Persze

zzrek · 2012. Jan. 2. (H), 18.23
Persze, ezzel tisztában vagyok, de ennél azért többre is képesek lehetnek a parserek. Nem csak sorban tudnak menni a jeleken és nyilván nem is regexp módszerrel értékelnek ki, hanem ennél speciálisabb módon (ez nyilván változik megoldásról megoldásra, minden gyártó az optimálisat próbálja megtalálni).

Nem is a parser működése a lényeg, hanem a szabvány, aminek megfelelően kell értelmeznie az állományt a parsernek. Eddig nem találtam olyan elemet ami miatt ne lehetne akár el is hagyni a "function" kulcsszót. Persze nem akarom ezt erőltetni, csak úgy eszembe jutott.
14

Pedig van, javascript nem

inf · 2012. Jan. 3. (K), 07.33
Pedig van, javascript nem veszi olyan szigorúan a sor végi ;-ot, szóval:

var x = (y){alert("x");}
két dolgot is jelenthetne:

a.)

var x = y;
alert("x");
b.)

var x = function (y){alert("x");};
15

Erről beszéltem én is

zzrek · 2012. Jan. 3. (K), 12.34
Ilyesmiről beszéltem én is, hierarchia kérdése az egész.
Az a=2+3*5 is értelmezhető lenne kétféleképp, ha nem tudnánk, hogy a szorzás "erősebb".
A "zárójel után kapcsos" lehetne a szabvány szerint függvénydeklarációként értelmezve és kész.
Egyébként is az általad írt a.) értelmezés szerint a kapcsos zárójeles rész egy jelöletlen funkciójú blokkot definiál, amiben önmagában nem sok logika van, sőt, a sima zárójeles résznél is felesleges a zárójel (ha jól emlékszem a zárójel után mindenképp kéne legalább egy "új sor", ha nincs pontosvessző). Ergo nyilvánvaló, hogy függvénydefinícióról van (lenne) szó.
16

kapcsos zárójeles rész egy

inf · 2012. Jan. 3. (K), 13.57
kapcsos zárójeles rész egy jelöletlen funkciójú blokkot definiál, amiben önmagában nem sok logika van

Hát én nem szoktam használni, de attól még egy ugyanolyan nyelvi elem, mint az összes többi... :-) Persze tényleg lehetne sorrendet állítani a kettő között, vagy akár kikötni a ; használatát...

Ettől függetlenül én csak arra reagáltam, hogy azt mondtad, hogy nincs olyan nyelvi elem, amivel ütközik. Márpedig van... :-)
17

Vagyis

zzrek · 2012. Jan. 3. (K), 15.28
Vagyis nem lenne nehezebb bevezetni a "semmi" használatát mint a #-et, vagy bármi mást, a parser meg tudja oldani ha kell ;-)
18

Persze, a parser mindent meg

inf · 2012. Jan. 3. (K), 15.54
Persze, a parser mindent meg tud oldani, csak jól kell megírni. :-) Szerintem a javascript így is az egyik legrugalmasabb nyelv...

Anno még gondolkodtam valami ilyesmin:

$({
	"namespace My.Test.Namespace":
	{
		"class MyClass":
		{
			"print(String, Number)": function (str, num)
			{
				alert(str + num);
			}
		},
		"class TestClass extends MyClass":
		{
			"print(Number)": function (num)
			{
				this.parent.print("A szám: " + num);
			}
		}
	}
});

var test = new My.Test.Namespace.TestClass();
test.print(13);
Igazából annyira nem is nehéz megvalósítani mindezt, csak hát az a probléma vele, hogy gyakorlatilag egy új nyelvet kreálunk javascript alatt, az meg annyira nem fun. :-) Ezért inkább elvetettem az ötletet. Jobban örülnék, ha mondjuk a Function.prototype-ot egészítenék ki az öröklődéssel, meg esetleg valahogy megtámogatnák a típusellenőrzést. Nekem több nem is nagyon kéne...

Na jó mégis kéne, mondjuk hashCode az Object.prototype-ba még jó lenne... De több már tényleg nem :D
19

Én is

zzrek · 2012. Jan. 3. (K), 16.51
Én is szeretem a javascript rugalmasságát, valószínűleg ki sem használom a lehetőségeit, így nem sokat gondolkodok azon, hogy mi hiányzik belőle ;-)
5

Határozottan egyetértek!

presidento · 2012. Jan. 1. (V), 22.33
Az összegzésben kiemeltem, hogy a feladat teljesítve, de gyakorlatba nem került, egyfajta proof of concept megoldásról van szó. Abban is egyetértek, hogy JavaScriptben az ember lehetőleg JavaScript szemlélettel programozzon, és az is igaz, hogy ezzel nagyfokú rugalmasság érhető el.

Amit a cikk elején írtam, és amit te is idéztél az amiatt van, mert a JavaScript nem támogatja a saját szemléletét, ugyanis nyelvi szinten nem tesz különbséget a függvény (értsd: objektumhoz nem kapcsolódó futtatható nyelvi elem) és a metódus (értsd: objektumhoz kapcsolódó futtatható nyelvi elem) között. Miért mondom ezt?

Ahhoz, hogy a JavaScript objektum orientált világképét megértsük, két dolgot kell világosan látni:
  1. a prototípus alapú öröklődést (ez nem tárgya ennek a bejegyzésnek és jól működik)
  2. a metódusok kezelését / viselkedését.

Az osztály alapú objektum orientáltságnál amikor létrehozok egy metódust, az fixen hozzá van kötve az osztályhoz (pontosabban annak példányaihoz egyenként). JavaScriptben a metódus meghívásának pillanatában dől el, hogy éppen melyik objektumhoz kapcsolódik, és ezt az objektumot a metóduson belül a this változóval érhetjük el. Ez az, amit nem biztosít a JavaScript, hiszen ha egy metódus belsejében megjelenik valahol a function (ez pedig lényegi része a nyelvnek az egyszálúságnak és az aszinkronitásnak köszönhetően), abban a blokkban bár a metóduson belül vagyunk, a this mégsem arra az objektumra mutat, amihez a metódusunk a meghívás pillanatában kapcsolódik. Önellentmondás. Ez az, ami inspirálta a különféle hack-eléseket (_this, that, és hogy az osztály alapú objektum orientáltságot megpróbáljuk valahogyan belevinni).

Ennek a szemléletnek a helyes működését biztosítja a CoffeeScript biztosít azáltal, hogy lehetőséget ad metódusok létrehozására (a ->-lal), és a metódusokon belül függvények használatára (a =>-lal). Így CoffeeScriptben tudunk igazán JavaScript szemlélettel programozni. (Ezt fejtettem ki az új nyelvi elem fejezetben is, de szerintem azokra nem célszerű építeni a közeljövőben.)
8

ugyanis nyelvi szinten nem

inf · 2012. Jan. 2. (H), 05.37
ugyanis nyelvi szinten nem tesz különbséget a függvény (értsd: objektumhoz nem kapcsolódó futtatható nyelvi elem) és a metódus (értsd: objektumhoz kapcsolódó futtatható nyelvi elem) között

Meg harmadikként még idetenném az osztályt is, mert ugye azt a konstruktor függvényével adjuk meg... Jó sok munkával ez a három elválasztható egymástól.

Egyébként én nem értem, hogy miért foglalták le például a class-et és az extends-et, mint kulcsszót, amikor nem is használják őket...
3

Nagyon jó volt, tetszett!

zzrek · 2012. Jan. 1. (V), 20.34
Köszi, jó volt végigolvasni.

( A végén a "mintha a fentebb kifejtett this módszert használnánk" nem inkább that akar lenni? )
6

nem

presidento · 2012. Jan. 1. (V), 22.37
Nem, ugyanis nem objektum-szintű változót hoz létre, hanem a függvény meghívásakor aktuális this-t őrzi meg végig a metódus során, mintha a _this-t használnánk.
20

"with statement"

Karvaly84 · 2012. Jan. 7. (Szo), 09.13
Ha már a OOP és JavaScript, lenne egy evvel kapcsolatos kérdésem.

Én szoktam alkalmazni a with operátort az osztálymetódusokban, hogy ne keljen százszor ki írni a this változót, illetve a "use namespace" helyett. Erről mit gondoltok? Azért kérdezem mert ugyan sok helyen nem látom használni a with operátort de szerintem sokkal könnyebb vele dolgozni mint orba-szájba this-t írogatni. Van ennek valami mellékhatása a teljesítményre?
21

Ne használd.

presidento · 2012. Jan. 7. (Szo), 10.01
Valójában nem lesz jelentősen kényelmesebb, viszont jóval nehezebb lesz kibogozni a kódod. Már minden komolyabb szereplő rájött, hogy nem volt jó ötlet beletenni ezt a lehetőséget a JavaScript-be, a strict módban le is van tiltva. Eddig egyetlen olyan esettel találkoztam, ahol indokolt volt a használata: bizonyos framework-ök esetén, ahol más objektumokkal kellett dolgozni. De jobb kikerülni, ez amolyan deprecated feature. ;)
22

Egyrészt átláthatatlan lesz

inf · 2012. Jan. 7. (Szo), 11.19
Egyrészt átláthatatlan lesz tőle a kód másrészt meg azt hallottam róla (talán 10 éve), hogy erőforrás igényes és lassítja a scriptet. (Ez utóbbiról nem tudok mit mondani, mert sosem használtam vagy mértem.)
38

Egyrészt új scope-ot hoz

tgr · 2012. Jan. 13. (P), 19.27
Egyrészt új scope-ot hoz létre, ami drága, másrészt egy csomó minden optimalizálhatatlanná válik, mert nem tudod fordítási időben eldönteni, hogy egy változó a globális scope-ból vagy a with paraméteréből jön. Ami egyrészt lassít (ebben a tesztben FF8-on pl. egy huszas faktorral), másrészt a jsmin és hasonló tömörítők se tudják kezelni. Ha ez nem zavar (pár nagyságrend lassulás végülis nem sokat számít, ha nem valami intenzív algoritmus belső ciklusában vagy éppen), a stackoverflow-n van pár szellemes példa arra, milyen helyzetekben nem árt a kód olvashatóságának a with használata.
23

Azt, hogy nem kényelmesebb,

Karvaly84 · 2012. Jan. 7. (Szo), 14.06
Azt, hogy nem kényelmesebb, és átláthatóbb nem hiszem, mert más nyelveknél sem kell feltétlen this-t írogatni csak ha fedve van. De attól még hiszek nektek, hogy elavult, és nem javasolt, ezért is kérdeztem meg. Csak roppantul kényelmetlennek érzem mikor minden metódusban avval kezdek, hogy rövíditek.
24

Nincs kód kiegészítőd?

inf · 2012. Jan. 7. (Szo), 17.26
Nincs kód kiegészítőd?
25

De van, csak most ismerkedem

Karvaly84 · 2012. Jan. 7. (Szo), 17.30
De van, csak most ismerkedem a vim, illetve az emacs szerkesztővel, és igaz az is idomítható, de probálom megtanulni a fapados verzióját. Nem sok sikerrel...
26

Szerintem próbálkozz

inf · 2012. Jan. 7. (Szo), 18.17
Szerintem próbálkozz másikkal, csak találsz valami olyat, amit tudsz is használni... Nekem általában az jön le ezeknél a szoftvereknél, hogyha valamelyiket nehéz megtanulni használni, akkor az rosszul tervezett, és általában egy idő után mindig találok olyat, ami nekem fekszik ...
27

Szerintem nem rosszul

Karvaly84 · 2012. Jan. 7. (Szo), 19.11
Szerintem nem rosszul tervezett, csak nem nekünk tervezték, hanem a 30 évvel ezelötti fejlesztőknek. Igazából megmondom a frankót: le akarok szokni az olyan IDE eszközökről, amik jobban tudják nálam, hogy akarok kódolni, tab-ot akarok e használni vagy space-t meg ilyenek. Elegem van ezekből, de az integrálhatóság az kell, csak minden ki kapcsolható legyen. De ez már nem (IDE) tartozik :D
28

Én meg pont a fordítottja

inf · 2012. Jan. 7. (Szo), 21.13
Én meg pont a fordítottja vagyok: nem akarom tudni, hogy tabot vagy space-t használok, mert egyáltalán nem érdekel :D A lényeg, hogy behúzás jelenjen meg... A netbeans-ben pl a kódformázást bírom a legjobban. Az külön energia befektetés, ha azzal kell foglalkoznod, hogy megfelelő formára hozd a kódodat. Itt meg egy kattintás, és megvan, nem kell vele különösebben foglalkozni... Persze azért örülnék, ha többféle formázási stílust be lehetne állítani, meg ilyesmik, de egyelőre megelégszem a beépítettel, és amit nem hittem volna, elég hamar megszoktam, hogy a blokk kezdő "{" előtt nincs sortörés ... :-)
29

Lehet módosítani

Poetro · 2012. Jan. 7. (Szo), 21.17
Én legalábbis úgy emlékszem, az alatt a pár óra alatt, amit a NetBeans-szel töltöttem, hogy elég jól testre lehet szabni a formázást. Persze lehet hogy az Eclipse volt, hasonló időt töltöttem el mindkettővel (értsd: 1 napnál kevesebbet).
30

Eclipse

MadBence · 2012. Jan. 7. (Szo), 21.31
Az Eclipse-t eléggé testre lehet szabni, {,( jelek előtti/utáni soremelés, white-space-ek, bármilyen kódolási stílust rá lehet kényszeríteni
35

Nekem az Eclipse PHP-plugin

bugadani · 2012. Jan. 8. (V), 15.39
Nekem az Eclipse PHP-plugin annyit enged beállítani, hogy hány szóközt tegyen tab helyett. Ilyenkor mit lehet tenni?

Ja, hogy benéztem, és JS-ről volt szó... Bocsi, azért ha valaki tud valami választ a kérdésemre, ne tartsa vissza.
36

Bár nincs PHP pluginem, de

MadBence · 2012. Jan. 8. (V), 15.47
Bár nincs PHP pluginem, de szerintem ugyanazokat lehet beállítani, mint a Javás verzióban.
Preferences->PHP->Code Style->Formatter->Edit
Ugyanazokat a beállításokat tudja, mint a NetBeans.
37

Formatter alatt ennyi van:

bugadani · 2012. Jan. 8. (V), 15.59
Formatter alatt ennyi van: Tab policy és Indentation size. Ezen kívül Restore defaults és Apply gomb. Code style-n belül ezen kívül csak a kódsablonok vannak. Néhány beállítás elszórva az Editor alatt, de semmi olyan szintű, mint amire a NetBeans / Eclipse-Java képes. Azért köszönöm.
31

Egy éve mindent eclipse-be

Karvaly84 · 2012. Jan. 7. (Szo), 22.03
Egy éve mindent eclipse-be írok. De hiába állítom át globálisan a text editor-t (insert space for tab pipa kikapcs). Ant(xml), xslt, xml, html, java, javascript, mind más konvenciókat követ, és időnként a sok plugin miatt újra kell tákolni az egész IDE-t. Persze nem lehet lebutítani fapadosra a szerkesztést. Nincsenek nagy elvárásaim, csak annyi ha a tab-ot leütöm az ne négy darab szóköz legyen hanem 8 karakter hússzú behúzás tab-ot használva, ha leütöm xml-nél négyszer a space-t akkor az meg ne legyen U+0009 csak ha tab-ot ütök le, és ha 2000 karaktert egy sorba akarok írni az legyen egy sorba, és pl a <code> címkét a html szerkesztő ne blokk elemnek nézze már. Viszont a debuggertől, a console-tol meg a kis external tools apróságoktól nem akarok megválni. Na de nagyon elragadtattam magam, bocsesz...
32

Az vagy eclipse volt, vagy

inf · 2012. Jan. 8. (V), 12.32
Az vagy eclipse volt, vagy nem a php-s változata a netbeansnek (már ha van különbség a változatok között).
33

Rengeteg mindent be lehet

Poetro · 2012. Jan. 8. (V), 14.48
Rengeteg mindent be lehet állítani NetBeans-ben. És ez a PHP-s változat, de egyébként nincs különbség, csak a telepített pluginekben.
34

Ohh, ezt benéztem :-) Csak

inf · 2012. Jan. 8. (V), 15.06
Ohh, ezt benéztem :-) Csak átfutottam anno az options-nek ezt a részét, aztán észre sem vettem, hogy a kategória változtatásnál még van jópár opció...