Egy régebbi melós projekt során merült fel a dolog, aztán azóta még nem volt nagyon időm írni róla, pedig akkor eléggé rákattantam a témára. Aztán most, hogy újraírtam a deadlime bejegyzés értékelő rendszerét valami korrektebb formába (azok a kis csillagok jobbra a bejegyzés után...), gondoltam ejtek néhány szót az objektum-orientált JavaScript szépségeiről.

A "JavaScriptben programozás szépségei", mint olyan, nem nagyon merült fel bennem soha, ha ezzel a nyelvvel kellett foglalkoznom (bár ebben biztos az is szerepet játszott, hogy még a különböző keretrendszerek elterjedése előtt az embernek if ágak egész erdejét kellett írnia, hogy a műve végül a főbb böngészőkben működjön). Szerencsére ezen sikerült változtatni.

A dolog alapjait egy, a weblaboron megjelent cikk elég jól leírja, én most inkább egy másik megközelítést szeretnék megemlíteni, amit ha a prototype.js-el együtt használunk, egy sokkal szebb szerkezetet kapunk.
Kezdetben ugye volt nekünk a függvény, mint olyan, amit egy alternatív módon is meg tudtunk adni:

var f = function(param)
{
    alert('param: '+param);
};

f('valami');

Aztán van olyan dolog is a JavaScript-ben, hogy asszociatív tömb, amit valahogy így kell elképzelni:

var a = {
    a : 'a értéke',
    b : 'b értéke',
    c : 'c értéke'
};

alert(a['a']);

A dolog ott kezd érdekessé válni, hogy ugye minden ilyen tömbelem elérésre létezik egy alternatív módszer is, ami már olyan "objektum-orientált" szerű:

var a = {
    a : 'a értéke',
    b : 'b értéke',
    c : 'c értéke'
};

alert(a.a);

Itt jön a gondolat, hogy mi lenne, ha ezt a két módszert összekombinálnánk, és akkor legalább már ez az OOP dolog annyiban megvalósulna, hogy a függvényeket egy csokorba tudjuk gyűjteni:

var c = {
    f : function(param)
    {
       alert('param: '+param);
    }
};
c.f('valami');

Most már mondhatni alakul a dolog, de hát még a példányosítás hiányzik. Itt jönnek a képbe a függvények (tehát ezen a ponton már érdemes a weblabor cikk által nyújtott tudás birtokában lenni), amikkel lehet ilyen perverz dolgokat is művelni:

var f = function(param)
{
    this.out = param;
    
    this.alert_me = function()
    {
        alert('param: '+this.out);
    };
};

var p = new f('valami');

p.alert_me();

Na kérem szépen, ez már egészen OOP, már csak helyre kell pofozni, mert ez így nem kicsit csúnya. Tehát, van a minden függvényenek alapból egy prototype tulajdonsága (aminek tökéletesen semmi köze nincs a prototype.js keretrendszerhez), aminek ilyen asszociatív tömb formában szépen meg lehet adni a függvény további tulajdonságait is. Valahogy így:

var c = function(param) {
    this.out = param;
};
c.prototype = {
    alert_me : function()
    {
        alert('param: '+this.out);
    }
};
b = new c('másvalami');

b.alert_me();

A dolog csúnyasága csak annyi, hogy a konstruktor és a többi függvény el van választva egymástól. Ezen segít a prototype.js, aminek van egy Class.create() függvénye. Ennek segítségével az osztályunk initialize() függvénye automatikusan meghívódik új példány készítése esetén, és átadódnak neki a paraméterek:

var c = Class.create();
c.prototype = {
    initialize : function(param)
    {
        this.out = param;
    }
    alert_me : function()
    {
        alert('param: '+this.out);
    }
};
b = new c('másvalami');

b.alert_me();

Emiatt az egy dolog miatt mondjuk még nem lenne érdemes a prototype.js -t használni, mivel ez a része csak ennyiből áll:

var Class = {
    create : function() {
        return function() {
            this.initialize.apply(this, arguments);
        };
    }
};

De mivel egyéb jó tulajdonságokkal is rendelkezik, ezért nézzünk is meg még egy kis példakódot, hogy jobban előjöjjenek a dolog szépségei:

var Gomb = Class.create();
Gomb.prototype = {
    initialize : function(id)
    {
        this.object = $(id);
        
        Event.observe(
            this.object,
            'mouseover',
            this.evt_over.bindAsEventListener(this)
        );
        Event.observe(
            this.object,
            'mouseout',
            this.evt_out.bindAsEventListener(this)
        );
        Event.observe(
            this.object,
            'click',
            this.evt_click.bindAsEventListener(this)
        );
    },
    evt_over : function(evt)
    {
        alert(this.object.id+' mondja: Hurrá, fölöttem van az egér!');
    },
    evt_out : function(evt)
    {
        alert(this.object.id+' mondja: Már nincs :\'(');
    },
    evt_click : function(evt)
    {
        alert(this.object.id+' mondja: Köszi, hogy megnyomtál!');
    }
};

var my_button = new Gomb('id_button');

A használt prototype.js függvényekről további információ: Class.create(), Event.observe(), Function.bindAsEventListener(). Ennyit mára, JavaScriptekben gazdag további szép napot. :)