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. :)