ugrás a tartalomhoz

Egy JavaScript keretrendszer születése – natív osztályok

inf3rno · 2010. Okt. 5. (K), 08.40
Egy JavaScript keretrendszer születése – natív osztályok

Nemrég tett közzé Farkas Máté (fmate14) blogbejegyzést tömb bejárási problémákról, amelyhez kapcsolódóan Galiba Péter (Poetro) szóba hozta, hogy létezik egy hasOwnProperty() nevű Object metódus. Kicsit jobban utánajártam, és kiderült, hogy MSIE nem támogatja, a többi nagyobb böngésző viselkedése pedig néhány esetben eltérő, ezért úgy döntöttem, hogy egy az egyben felülírom ezt a metódust.

Object.hasOwnProperty

A sorozatban megjelent

A bevezetőben isKey() lokális függvény volt, amiből most Object.hasOwnProperty() lesz. Igazából ez Object.prototype.hasOwnProperty(), de a prototype-ot nem teszem ki, mert statikus metódusokat (osztályfüggvényeket) sem most, sem a későbbiekben nem fogok használni. Ennek az az oka, hogy a statikus metódusok ugyanott kapnának helyett, mint az osztályokra jellemző metódusok. Például: a statikus Array.append() felülírná a ClassBuilder.classMethods.append()-et az Array osztályban. Persze az Array még nem igazi – csak natív – osztály, de a fejezet végére már az lesz.

Object.prototype.hasOwnProperty=function (k)
{
	var p=this.constructor.prototype;
	return (k in this) && (!(k in p) || p[k]!==this[k]);
};
Object.prototype.getClass=function ()
{
	return this.constructor;
};

Mint látható, bekerült még az Object.getClass(), ami a konstruktort adja vissza. Ez azért volt szükséges, mert már osztályokról és nem konstruktorokról beszélünk. Az isKey(o,i) helyett pedig mindenhol áttértünk a o.hasOwnProperty(i)-re. Ha összehasonlítjuk a két függvényt, akkor látható, hogy nem teljesen ugyanazt csinálják. Az isKey() csak az Object.prototype tulajdonságait szűri az objektumokból, a hasOwnProperty() pedig az aktuális osztály prototípusának a tulajdonságait is. Ez utóbbiba beletartoznak az Object.prototype tulajdonságai is, hiszen a prototípusok is objektumok, tehát megkapják ezeket. Ennek a viselkedésnek az a következménye, hogy a ClassBuilder.clearFunctionMethods() egyszerűsödik:

clearFunctionMethods: function ()
{
	for (var j in Function.prototype)
	{
		if (Function.prototype.hasOwnProperty(j))
		{
			this.current[j]=undefined;
		}
	}
},

Mindent a maga idejében

A megvalósításnál elkövettem egy lényeges hibát.

Mivel egy bonyolult dolgot építünk fel, ezért a builder pattern a célravezető. A buildert indokolja az is, hogy vannak natív osztályok, melyekhez később szintén hozzá kell adni az osztályokra jellemző viselkedést.

Itt a hiba nem feltétlen a builder pattern használata volt, hanem amivel megindokoltam. Az indoklásnál arról beszéltem, hogy a későbbiekben valamikor natív osztályokhoz is hozzáadjuk a Class viselkedést. Ez rendben is van, viszont még egyáltalán nem volt szó arról, hogy ezt hogyan oldjuk meg. Szóval egy olyan dologgal indokoltam a mintát, ami valamikor a jövőben esedékes csak, és ki tudja mikor… Ilyen esetben általában igaz, hogy úgy kell megvalósítani a dolgokat, ahogy a mostani helyzet indokolja, ha pedig majd a jövőbeni dolgokat hozzá kell adni a rendszerhez, akkor ott és akkor refaktorálni kell. Így egy csomó felesleges kódot és agyalást megspórolhatunk.

Úgy döntöttem, hogy a director kódot (ez vezérli a buildert) a Class() függvényből átteszem a ClassBuilderbe, így gyakorlatilag egy factory jön létre.

var ClassFactory=
{
	classMethods:
	{
		append: function (o){/* ... */}
	},
	createClass: function ()
	{
		this.newClass();
		this.clearFunctionMethods();
		this.addClassMethods();
		return this.getResult();
	},
	newClass: function (){/* ... */},
	clearFunctionMethods: function (){/* ... */},
	addClassMethods: function (){/* ... */},
	getResult: function (){/* ... */}
};

var Class=function (o)
{
	if (this.constructor!==arguments.callee)
	{
		throw new SyntaxError("class=new Class(...) is the right syntax");
	}
	return ClassFactory.createClass().append(o);
};

Majd ha később tényleg kiválik a director, akkor visszanevezem ClassBuilderre.

Osztály leválasztása a függvényekről

Az osztályok még nem teljesen különültek el a függvényektől, szóval fejezzük be ezt a folyamatot. Az append()-nél egyelőre megengedjük, hogy osztály is bekerüljön a tulajdonságok közé. Ezen változtatni kell. Először írjuk át az osztályok constructor tulajdonságát. Lehet gondolkodni azon, hogy ezt hol tegyük meg a ClassFactoryben. Én úgy gondoltam, hogy az addClassMethods() elejére nyugodtan be lehet tenni ezt is. Viszont így már az addClassMethods() elnevezés nem teljesen fedi a valóságot, hiszen nem csak metódusokat adunk az osztályhoz. Szóval átírtam implementClassInterface()-re, és új nevet kapott a clearFunctionMethods() is, hogy az előbbivel összecsengjen.

createClass: function ()
{
	this.newClass();
	this.removeFunctionInterface();
	this.implementClassInterface();
	return this.getResult();
},

/* ... */

removeFunctionInterface: function ()
{
	for (var j in Function.prototype)
	{
		if (Function.prototype.hasOwnProperty(j))
		{
			this.current[j]=undefined;
		}
	}
},
implementClassInterface: function ()
{
	this.current.constructor=Class;
	for (var j in this.classMethods)
	{
		if (this.classMethods.hasOwnProperty(j))
		{
			this.current[j]=this.classMethods[j];
		}
	}
},

Ezek után az append()-et bővítettem ki az osztályokat ellenőrző kóddal.

append: function (o)
{
	if (!o)
	{
		return this;
	}
	for (var j in o)
	{
		if (!o.hasOwnProperty(j))
		{
			continue;
		}
		var v=o[j];
		if ((v instanceof Function) && v.constructor===Class)
		{
			throw new TypeError("Class property cannot be a Class.");
		}
		this.prototype[j]=v;
	}
	return this;
}

Natív osztályok

Most már nekiállhatunk a natív osztályoknak. Mit is kell velük csinálni? Mivel a natív kódba nem nyúlhatunk bele, annyit tehetünk, hogy töröljük a függvényekre jellemző felületet, és hozzáadjuk az osztályokra jellemzőt. Durvább beavatkozást nem érdemes csinálni.

Elsőnek bővítsük a bevezetőben létrehozott tesztünket:


test("native classes",function ()
{
	ok(Array.getClass()===Class,"Constructor ok.");
	ok(Array.call===undefined,"Function interface removed.");
	ok(!!Array.append,"Class interface implemented.");
});

Mivel a ClassFactoryben a dolgok nagy része már megvan, ezért nyilvánvaló, hogy a natív osztályra vonatkozó kód is ide fog kerülni. Csak módot kell adni a this.current natív osztályra való átállítására, a többi már megvan. Erre a célra hozzuk létre a ClassFactory.setClass() metódust.

setClass: function (current)
{
	this.current=current;
},

Ezután már csak le kell vezérelni a létrehozási (módosítási) folyamatot.


nativeClass: function (nativeClass)
{
	this.setClass(nativeClass);
	this.removeFunctionInterface();
	this.implementClassInterface();
	return this.getResult();
}

Factory szétbontása

Most van az a rész, ahol el kell gondolkodni, hogy szétbontsam-e újra a ClassFactoryt. Végül úgy döntöttem, hogy az átláthatóság kedvéért megteszem, így kiemeltem a nativeClass() és createClass() metódusokat, ezek lettek a ClassFactory, a maradék pedig a ClassBuilder. A ClassFactory most így néz ki:

var ClassFactory=
{
	createClass: function ()
	{
		ClassBuilder.newClass();
		ClassBuilder.removeFunctionInterface();
		ClassBuilder.implementClassInterface();
		return ClassBuilder.getResult();
	},
	nativeClass: function (nativeClass)
	{
		ClassBuilder.setClass(nativeClass);
		ClassBuilder.removeFunctionInterface();
		ClassBuilder.implementClassInterface();
		return ClassBuilder.getResult();
	}
};

Látható, hogy elég sok az ismétlődés, amit emeljünk ki külön metódusba.

var ClassFactory=
{
	createClass: function ()
	{
		ClassBuilder.newClass();
		return this.getResult();
	},
	nativeClass: function (nativeClass)
	{
		ClassBuilder.setClass(nativeClass);
		return this.getResult();
	},
	getResult: function ()
	{
		ClassBuilder.removeFunctionInterface();
		ClassBuilder.implementClassInterface();
		return ClassBuilder.getResult();
	}
};

Kész is. Így már átlátható, hogy mi vezérli a létrehozást (ClassFactory) és foglalkozik annak részfolyamataival (ClassBuilder).

Natív osztályok átformálása

Most szedjük össze a natív osztályokat, és végezzük el mindre a módosításokat. Nincs mi alapján kiválasztani a globális névtérből, hogy mi natív osztály, és mi nem, szóval jobb, ha csinálunk egy listát róluk.

var nativeClasses=
[
	Boolean,
	Number,
	String,
	Array,
	RegExp,
	Date,
	Error,
	TypeError,
	ReferenceError,
	RangeError,
	SyntaxError,
	EvalError,
	URIError,
	Object,
	Function,
	Class
];
for (var j=0,l=nativeClasses.length; j<l; ++j)
{
	var nativeClass=nativeClasses[j];
	ClassFactory.nativeClass(nativeClass);
}

Igen, a Class is közéjük került. Esetleg el lehet gondolkozni azon, hogy a ClassFactory.classMethods-ból áttegyük a dolgokat a Class.prototype-ba, így az osztályok tulajdonságai is bővíthetőek lennének az append() használatával.

Egyelőre futtassuk a tesztet. Hoppá, jelez, hogy gond van: SyntaxError (Function interface removed.). Ha alaposabban megnézzük a removeFunctionInterface()-t, akkor hamar kiderül, hogy a for-in nem megy végig a natív metódusokon, így kénytelenek vagyunk kézzel felülírni őket. Szimplán a delete() nem lenne elég, mert a Function.prototype-ból jönnek ezek a dolgok.

removeFunctionInterface: function ()
{
	this.current.apply=undefined;
	this.current.call=undefined;
	this.current.toString=undefined;
	this.current.toSource=undefined;
	this.current.valueOf=undefined;

	for (var j in Function.prototype)
	{
		if (Function.prototype.hasOwnProperty(j))
		{
			this.current[j]=undefined;
		}
	}
},

Csinálhatnánk tömböt is ezeknek a tulajdonságoknak, de én úgy gondoltam, hogy annyira azért nincs sok belőlük.

Futtassuk a tesztet ismét. Minden okés.

Class.prototype

Na, most fontoljuk meg a classMethods áthelyezését. Ehhez módosítani kell az implementClassInterface()-t,

implementClassInterface: function ()
{
	this.current.constructor=Class;
	for (var j in Class.prototype)
	{
		if (Class.prototype.hasOwnProperty(i))
		{
			this.current[j]=Class.prototype[j];
		}
	}
},

és áttenni az append()-et.

Class.prototype=
{
	append: function (o){/* ... */}
};

Futtassuk a tesztet. Hamar kiderül, hogy itt is gond van: SyntaxError (class=new Class(...) is the right syntax). Ez azért van, mert a Class.prototype.constructor most az Object, mert egy objektumot adtunk meg prototípusnak. A Class példányok pedig ezt a viselkedést öröklik, és ezért elhasal a szintaxis ellenőrzőnk.

var Class=function (o)
{
	if (this.constructor!==arguments.callee)
	{
		throw new SyntaxError("class=new Class(...) is the right syntax");
	}
	return ClassFactory.createClass().append(o);
};

Ugye ilyenkor az arguments.callee a Class, a this.constructor pedig az Object. Ha átállítjuk a konstruktort Classra, akkor pedig a implementClassInterface()-ben a hasOwnProperty() nem fog átengedni egyetlen tulajdonságot sem, hiszen a Class.prototype-ot hasonlítja majd össze saját magával.

Én úgy döntöttem, hogy a gordiuszi csomó kibogozása helyett inkább átvágom azt. Szerintem a new Class(/* ... */) szintaxis megőrzése nem annyira fontos, szóval nem kezdtem áthidaló megoldás gyártásába, hanem inkább töröltem a szintaxist ellenőrző kódot. Veszteni nem vesztettem vele semmit, hiszen az osztályok építését a ClassFactory vezérli, a Class() függvényben pedig sehol máshol nem kerül elő a this kulcsszó.

var Class=function (o)
{
	return ClassFactory.createClass().append(o);
};

Így most már meg lehet hívni függvényként is a Classt, kinek ahogy tetszik.

 
inf3rno arcképe
inf3rno
Jánszky László JavaScripttel 1997 óta, PHP-vel 2005 óta foglalkozik, ismereteit autodidakta módon szerezte.
1

pici helyesbítés

inf3rno · 2010. Okt. 5. (K), 11.52
A végén a ClassFactory elnevezés megtévesztő lehet, igazából ClassDiretor-nak akartam elnevezni azért, hogy jelezzem, hogy az a gyártónak a rendező/vezérlő része. Én legalábbis úgy értelmeztem a builder patternt, hogy többek között ez a szétválasztás, ami fontos benne. Ha lehetne (szépen) örököltetni, akkor valószínűleg más lenne az egész szerkezete.
2

hasOwnProperty

Poetro · 2010. Okt. 5. (K), 20.05
Tudtommal 5.5 óta támogatott Internet Explorerben a hasOwnProperty legalábbis Object esetén. Így:
var hop = Object.prototype.hasOwnProperty,
    fp = Function.prototype,
    j;
//...
for (var j in fp) {  
  if (hop.call(fp, j)) {
    this.current[j] = undefined;  
  }  
} 
Ezért nem kell felüldefiniálni a hasOwnProperty-t
3

Igaz.

inf3rno · 2010. Okt. 5. (K), 20.58
Ciki, de tényleg :D
Én itt néztem, aztán ebből következtettem arra, hogy biztos azért tol
Object doesn't support this property or method
üzentet ie, mert nem támogatja. Legközelebb kipróbálom mielőtt ilyet írok. :-)

Ha Object.prototype viszi, akkor Function.prototype-al sem lesz gond, csak az olyanoknál, mint a window,document,dom nodek..., mert ie valamiért nem Object-ből származtatja ezeket....
(Ezt ie8-ra mondom, nem tudom a régebbi verziókban hogy van.)

Mondjuk sok fennakadást nem okoz a dolog, mert dom-nál úgysem használna ilyet az ember. Ahogy nézem a NodeList sem örökli, szóval azt is csak mint tömböt lehet iterálni.

ECMAScript-262 revision 3-ban adták hozzá a hasOwnProperty-t a szabványhoz 1999-ben, a safari implementálta utolsóként a nagyobb böngészők közül (2005 végén), és ahogy nézem a többi alkalmazás is támogatja már.

Ha valaki használ olyan alkalmazást, ami nem támogatja, akkor kérem szóljon, addig is törlöm a kódból githubon, a cikkben meg eldöntjük mi lesz.

Kösz, hogy szóltál.
4

Kérdés

janoszen · 2010. Okt. 5. (K), 22.35
Egy kérdésem van, bár ez lehet, hogy igazából a JS-ben való jártasságom hiányát jellemzi: valóban szükség van ilyen bonyolult működési logikára? Mármint persze nem a JS-ben írt komplett desktop rendszerekre gondolok, hanem a web nagy részét kitevő weboldalakra. Nekem valahogy sokkal jobban bejött a natív JS-ben kódolt egymástól relatíve független modulok halmaza, amik diszkrét módon csinálnak valamit az oldalon.

Szóval villanyosítsatok föl légyszi, hogy miért lesz ettől valakinek jobb az élete (hacsak nem azért, hogy a máshonnan ismert mintákat szuszakolja bele a JS-be).
5

Az alap rendszert nem

inf3rno · 2010. Okt. 6. (Sze), 00.37
Az alap rendszert nem feltétlen böngészőben futó dolgokra csinálom, nem is igazán lenne értelme, egy jquery vagy egy prototype.js bőven elég szinte bármelyik oldalhoz.
6

Túl nagy

janoszen · 2010. Okt. 6. (Sze), 06.31
De én még ezeket is túl nehézkesnek érzem. Persze értem, hogy az a cél, hogy elfedje a JS sajátosságait, de ezt az OOP eröltetést, amit a Prototype kódjában is látok, nem értem.
9

Hát nem is tudom igazán, hogy

inf3rno · 2010. Okt. 9. (Szo), 02.18
Hát nem is tudom igazán, hogy mit mondjak erre. A többi keretrendszerhez képest tényleg sokkal nagyobb az alapokat bővítő rész. Még akár prototype.js-hez is. Én úgy gondoltam, hogy érdemes bővíteni.


Leírom ide, hogy mik lesznek, és döntsétek el ti, hogy folytassam e a rendszer bemutatását, vagy nincs rá érdeklődés. Azt hiszem érthető, hogy azt a kevés szabadidőmet nem szívesen fordítom olyan cikkek írására, amiket senki sem olvas.


csomagok, típusellenőrzés alap:

this.append( //global ns is csomag felületet kap
{
	Splitter: new Package(
	{
		types:
		{
			s: new Type(String,Null), //összetett típusnál a tartalmazott típusokat kell felsorolni
			toArray: Array //függvény visszatérő érték típusához a függvény nevét kell megadni
		},
		toArray: function (s)
		{
			return (s || "").split(",");
		}
	}
});

Splitter.toArray("my,string"); // ["my", "string"]
Splitter.toArray(); // [""]
osztályok, felület és típus öröklés

this.append(
{
	Org: new Package(
	{
		Test: new Package(
		{
			Com: new Interface(
			{
				types:
				{
					name: Object,
					setName: Null
				},
				initialize: function (name){},
				setName: function (name){},
			})
		})
	})
});

Org.Test.Com.setName.debug(); // "Null Org.Test.Com.setName(Object name)"

Org.Test.append(
{
	MyAbstractCom: new Class(
	{
		types:
		{
			name: String
			//örökölt típus felülírásánál csak szűkíteni lehet a régi értéket
				// Object -> String megengedett, de String -> Object már nem, mert az Object minden osztályt magába foglal
		},
		initialize: function (name)
		{
			this.setName(name);
		}
	}).implement(Org.Test.Com) //interfész implementálás
});

Org.Test.append(
{
	MyCom: Org.Test.MyAbstractCom.extend( //öröklés, ilyenkor az ős interfészeit, típusait, mindent örököl...
	{
		types:
		{
			getName: String
		},
		setName: function (name)
		{
			this.name=name;
		},
		getName: function ()
		{
			//osztályra jellemző metódus
			return this.name;
		}
	})
});

var myCom=new Org.Test.MyCom("lol");
myCom.getName(); // "lol"
myCom.getClass().getPackage().getName(); //"Org.Test"

Object.typeOf(myCom); //true
String.typeOf(myCom); //false
Org.Test.Com.typeOf(myCom); //true
Org.Test.MyAbstractCom.typeOf(myCom); //true
Org.Test.MyCom.typeOf(myCom); //true
ős-leszármazott kapcsolat

var A=new Class(
{
	initialize: function ()
	{
		this.currentClass(); // A
		this.currentMethod(); // A.initialize
		this.superClass(); // null,
		this.superMethods(); // null
		
		this.currentMethod().ownerClass()===this.currentClass(); //true
		
		alert("A");
	}
});
var B=A.extend(
{
	initialize: function ()
	{
		this.superMethods().initialize(); //A.prototype.initialize()
		
		this.currentClass(); // B
		this.currentMethod(); // B.initialize
		this.superClass(); // A,
		this.superMethods(); // A.prototype
		
		alert("B");
	}
});
var C=B.extend(
{
	initialize: function ()
	{
		this.superMethods().initialize();
		
		this.currentClass(); // C
		this.currentMethod(); // C.initialize
		this.superClass(); // B,
		this.superMethods(); // B.prototype
		
		alert(this.currentClass().getName()); // "C"
		alert(this.getClass().getName()); // aktuális példány osztályának a neve
	}
});

var D=C.extend();

var c=new C(); //alert("A") , alert("B") , alert("C") , alert("C")
//az aktuális példány osztálya: C

var d=new D(); //alert("A") , alert("B") , alert("C") , alert("D")
//az aktuális példány osztálya: D
objektum azonosítás

var a={},b={},c={};

alert(b.hashCode()); //1
alert(a.hashCode()); //2
alert(c.hashCode()); //3

alert(b.hashCode()); //1
alert(a.hashCode()); //2
alert(c.hashCode()); //3
Nagyjából ennyi, amit alapból tud a rendszer, ezen kívül még lehet utólag módosítani interfészt, ős-osztályt, szal elég rugalmas... meg persze mindennek le lehet kérni a nevét a getName() -el...


Kérlek írjátok meg, hogy van e érdeklődés rá, vagy nincs.
7

Én teljesen egyet értek

snd · 2010. Okt. 6. (Sze), 12.06
Én teljesen egyet értek veled!
Olyan mintha Sopronba akarnék utazni Bp.-ről és mindezt Debrecenen keresztül tenném.
A js-ben nincsenek osztályok! Az hogy csinálunk egy Class-t attól még nem lesznek.
Valahol az első cikkben ez van:
ha a new kulcsszóval meghívott függvénynek visszatérő értéket állítunk be, akkor a kifejezés értéke a visszatérő érték lesz, és nem az újonnan létrehozott példán

és utána egy példa. Hát nem tudom. Szerintem próbáld ki:

alert(new Class()); 
// vs 
alert(Class()); 
Azzal, hogy minden natív objektumot kiegészítek és függvényeit felül írok, szerintem az
később több bajt okozhat, úgyhogy én csinnyán bánnék vele. Főleg a Object-re gondolok itt.
Ha írsz egy jó bonyolult keretrendszert, és szerintem oda tartasz, akkor a végén már nem js-ben kell programozz, hanem abban a keretrendszerben, aminek már szinte, csak annyi a köze a js-hez, hogy abban íródott. Főleg akkor nem a legjobb dolog ez, ha böngészőben szeretnéd használni. Szerintem minden programozási nyelvnek megvan a sajátossága, szépsége. A js-nek is. Én nem akarom úgy érezni magam mikor js-ben programozok, mintha c-ben vagy éppen python-ban tenném... Ameddig most eljutott a keretrendszer, ha jól látom, ezt küszöböli ki:

var MyObj = function MyObj (){}
MyObj.prototype.prop1 = null;
MyObj.prototype.fn1 = function MyObj_fn1(){};

var MyObj2 = function MyObj (){}
MyObj.prototype = new MyObj();
Ezt szerintem egy extend és egy apply függvény is megoldja. Az előbbit az öröklés segítéséra, a másikat a példányok tulajdonságainak módosítására.

Function.prototype.extend = function Function_extend (){ ... }
Function.prototype.apply = function Function_apply (){ ... }
Alapjában dicséretes, hogy ilyesmivel foglalkozol és köszönet illet, mert veszed a fáradtságot és leírod, hogy abból mindenki tanulhasson. Ritka az ilyen önzetlen ember. Szerintem a programozásnak a feladat pontos meghatározásával és a tervezéssel kell (kellene) kezdődnie. Azt gondolom (a cikkek alapján), hogy ezt kihagytad, vagy csak nem osztottad meg. Sajnos sokszor én sem teszek így.
8

Válaszok:

inf3rno · 2010. Okt. 8. (P), 23.50
A js-ben nincsenek osztályok! Az hogy csinálunk egy Class-t attól még nem lesznek.

Ezt én értem, de mivel az osztályok viselkedését próbálom modellezni a konstruktorokkal, ezért szerintem jogosan nevezhetem őket osztálynak, még ha nem is azok. Nyilván az ember egy konstruktort arra használ, hogy példányosítson vele, tehát arra, amire osztályokat is használna. A függvényeket viszont függvénynek használjuk, és nem példányosításra. Azért csináltam ezt a new Class(...) rendszert, hogy ez teljesen egyértelmű legyen a kódból is.

Több célom is van ezzel a keretrendszerrel.
  • Szeretnék osztályokat, interfészeket, csomagokat, típusellenőrzést megvalósítani, hogy átláthatóbb legyen a rendszeremmel írt kód, hogy meg tudjak csinálni olyan dolgokat, amik más nyelvekben alapvetőek, hogy egy kicsit növeljem a komfort érzetet, amikor js-ben programozok. Ennek az az ára, hogy megkötéseket kell tennem a core javascripthez képest, én ezt a saját módomon oldottam meg, mások meg máshogy. Nekem nem jelent problémát, hogy c-hez vagy python-hoz hasonló a megoldásom szintaxisa, sőt próbáltam direkt olyat választani, ami más nyelvek ismerőinek elég egyértelmű, hogy könnyebb legyen nekik js-el elindulni.

  • Szeretnék programtervezési mintákat használni javascriptben. Azért, mert tetszik, hogy js-ben is lehet ilyesmit, azért, hogy gyakoroljam ezeket a mintákat, és azért, hogy aki nem ismeri őket, az is valamennyire belekóstoljon a témába. Nem vagyok egy szakértő ezen a téren, ezért inkább azt írom, hogy én hogy látom ezeket a mintákat.

  • Nem szeretném támogatni, hogy bármilyen más keretrendszert mellette használjanak. Én nem fogok, ha más akar, azt meg kifejezetten ellenzem, nem csípem a copy-paste programozást, és általában az olyan emberek használnak egynél több keretrendszert egy js projekthez, akik egyikhez sem értenek, csak mindegyikhez találtak valami nekik tetsző kódot, amit le tudnak másolni.

  • Szeretnék SOAP-pal kommunikálni a szerverrel, és osztály példányokat szeretnék javascriptben kapni, nem ilyen-olyan formában kódolt adatokat, tömböket meg hasonlót. Ennek persze sok hátulütője lehet, de úgy vagyok vele, hogy legalább megpróbálom, aztán majd kiderül, hogy mire használható. Van elég sok ötletem ilyen téren.


Azzal, hogy minden natív objektumot kiegészítek és függvényeit felül írok, szerintem az
később több bajt okozhat, úgyhogy én csinnyán bánnék vele. Főleg a Object-re gondolok itt.

Mire gondolsz? :-)

Szerintem a programozásnak a feladat pontos meghatározásával és a tervezéssel kell (kellene) kezdődnie. Azt gondolom (a cikkek alapján), hogy ezt kihagytad, vagy csak nem osztottad meg.

Szándékosan nem osztottam meg, hogy milyen funkciókkal bővítem az alap javascriptet, úgy gondoltam, hogy elég unalmas lenne, ha a cikksorozat elején felsorolnám a bővítések listáját, utána meg egyenként megvalósítanám a részeket. Github-on tákolok egy dokumentációt a rendszerhez, ott fent lesz minden előbb, vagy utóbb, sőt azt hiszem, hogy most kiteszem, hogy még mi várható, aztán eldönthetitek, hogy kíváncsiak vagytok e rá, vagy sem.
10

Kicsit hasonló OO

zhagyma · 2010. Okt. 10. (V), 18.12
Kicsit hasonló OO implementációt valósít meg a qooxdoo és a tibco General Interface javascriptben. Aki kíváncsi, hogy mit lehet ebből kihozni az nézze meg például a qooxdoo demo lapojait. Az tény, hogy ez már nem kifejezetten javascript hanem keretrendszer programozás. Szerintem igazából a "class nélküli" OO implementáció illine jobban a javascripthez. Termászetesen ez szigorúan magánvélemény.

Szeretnék SOAP-pal kommunikálni


Szerintem ezzel az a probléma, hogy a kliens oldalon XML Schema implementációra lenne szükség. Tudomásom szerint ezt csak az IE nyújtja teljeskörűen egy browserben, bár a jelenlegi állkapotokat már nem ismerem XML Schema terén. A Mozilla úgyan megvalósított valamilyen egyszerűsített SOAP implemetációt, igazából nem tudom most hogy állnak ... Én végül hosszas töprengés után megmaradtam a kliens oldalon egy egyszerűsített XML-RPC megvalósításánál (well-formed, XML szerkezet validáció, típusellenőrzés viszont elsumkálova).

Igazából mindezzel azt szeretném csak mondani, hogy igen nagy fába vágtad a fejszédet ami mindig is elismerést vált ki belölem. Ha időd engedi folytasd bár Én sem vagyok időmilliomos ...
11

A kliens oldalon van XSLT

inf3rno · 2010. Okt. 10. (V), 19.42
A kliens oldalon van XSLT támogatás, szóval meg lehet oldani, hogy a wsdl és xsd fájlokat XSLT-vel javascript kódra transzformáljuk. Persze ugyanezt szerver oldalon is meg lehet tenni, aztán kesselni. Ha megvan, hogy milyen js kódra menjen a transzformáció, és megvannak az xsl fájlok, akkor utána már viszonylag simán megy a dolog. (Eddig még nem volt időm ezzel foglalkozni, szóval még csak elmélet.)

Ha megvan, hogy XSD-ből milyen js kód legyen, akkor utána meg lehet oldani JSON-al is az adatátvitelt, ami ugye sokkal tömörebb forma, mint egy soapEnv.

Amit szeretnék az az, hogy a validálás szerver és kliens oldalon egy forrásból menjen, tehát ne legyen redundáns (ne kelljen szerver és kliens oldalon ugyanazt az ellenőrző kódot kézzel megírni), mert az hibákhoz vezet.

(Amúgy ha xforms támogatott lenne a böngészőkben, akkor egy percet nem foglalkoznék tovább ezzel.)
17

Úgy gondolom ezt meg lehet

zhagyma · 2010. Okt. 10. (V), 21.26
Úgy gondolom ezt meg lehet így oldani, igaz soha nem próbáltam:

A kliens oldalon van XSLT támogatás, szóval meg lehet oldani, hogy a wsdl és xsd fájlokat XSLT-vel javascript kódra transzformáljuk.


De az XML Schema-t az XML file validációjára szerettem volna használni. Ez a lépés a cross-browser támogatás miatt átalakult az esetemben egy XML struktúra és típus / érték ellenőrzést megvalósító javascript eljárásra a kliens oldalon. Nyilván szebb lenne, ha a böngészőnek küldött xml és a kapcsolódó xsd file segítségével ezt a validációt a böngésző végezné.
20

Szerintem is.

inf3rno · 2010. Okt. 10. (V), 22.20
Nyilván szebb lenne, ha a böngészőnek küldött xml és a kapcsolódó xsd file segítségével ezt a validációt a böngésző végezné.


Persze, én is szeretném, ha több dologra lenne már beépített lehetőség, de sajnos nincs :S és az ez irányú fejlesztések is nagyon lassan haladnak...
18

Úgy gondolom ezt meg lehet

zhagyma · 2010. Okt. 10. (V), 21.27
Úgy gondolom ezt meg lehet így oldani, igaz soha nem próbáltam:

A kliens oldalon van XSLT támogatás, szóval meg lehet oldani, hogy a wsdl és xsd fájlokat XSLT-vel javascript kódra transzformáljuk.


De az XML Schema-t az XML file validációjára szerettem volna használni. Ez a lépés a cross-browser támogatás miatt átalakult az esetemben egy XML struktúra és típus / érték ellenőrzést megvalósító javascript eljárásra a kliens oldalon. Nyilván szebb lenne, ha a böngészőnek küldött xml és a kapcsolódó xsd file segítségével ezt a validációt a böngésző végezné.
13

SOAP

janoszen · 2010. Okt. 10. (V), 19.53
Csak úgy kiváncsiságból: minek Neked SOAP? Főleg: JavaScriptből. Nem egészen erre van kitalálva és akármennyire hypeolva van, nem erre való. (Hacsak nem valami intranet alkalmazást írsz, ahol nem számít annyira a response time.)
15

Szimplán annyi, hogy van egy

inf3rno · 2010. Okt. 10. (V), 21.16
Szimplán annyi, hogy van egy elképzelésem, amit meg szeretnék valósítani. Kb arról van szó, hogy a szerverrel csak soappal kommunikálnék, a kijövő soapEnv-eket meg kliens oldalon XSLT-vel alakítanám a megfelelő formára (html,js). Szóval a View réteget teljesen át szeretném tolni kliens oldalra, a sablonozáshoz pedig XSLT-t használnék.

Hogy ez mennyire lenne gyors vagy lassú, arról jelenleg fogalmam sincs, pont ezért kéne kipróbálni. A teljes view így statikus formában jönne, szóval a sablonozás terhe lejönne a szerverről. Az adatátvitel érdekesebb kérdés, egyrészt a statikus tartalmat lehet böngészőben kesselni, másrészt a javascript motornak egy xsl fájlt csak egyszer kell leszednie, nem pedig minden oldal generálásakor, viszont a soapEnv nem valami tömör forma (nyilván indent nélkül egy fokkal jobb), de mégsem egy JSON. Azt hiszem netes adatátvitelnél az lenne a jó, ha megcsinálnák a SOAP JSON-os megfelelőjét.
25

Nem véletlen

janoszen · 2010. Okt. 11. (H), 12.39
Nem véletlen, hogy a webes/weboldalas világ inkább a REST/JSON API-kat támogatja, a SOAP-nak inkább ott van jelentőssége, ahol tényleg bonyolult objektum struktúrák vannak.
26

Szerinted mi az oka ennek?

inf3rno · 2010. Okt. 11. (H), 12.46
Szerinted mi az oka ennek?
27

Struktúra

janoszen · 2010. Okt. 12. (K), 00.05
A SOAP nagyon durván vázolva tulajdonképpen abban különbözik az XMLRPC-től, hogy lehet benne objektumokat átadni. Ez ott poén, ha ezekeből az objektumokból egyből tudsz kódot gyártani. Ennek leginkább a Javas világban van kultúrája és eszközkészlete, a webes világban alig. A PHP SOAP interface-e pl. egy kalap tehéntrágya, se validáció nincs benne, se megírva nincs normálisan (nem működik a float, objektumokat referencia szerint hibásan továbbítja, stb). JavaScript, Flash dettó, hogy az XML parzolás pikantériáiról ne is beszéljek.

Egy JSON tömböt sokkal egyszerűbb parzolni és validálni is, sokkal kisebb az esélye, hogy hibát vétesz benne és gyorsabb is. Gyakorlatilag az összes webes nyelv (ActionScript kivételével, ami legendásan feature-szegény) implementálja a JSON-t, amíg a SOAP-ot értelmesen alig.

Hogy még egy harmadik dolgot mondjak, SOAP-ból az ember legtöbbször kódot generál, amit aztán nem szívesen módosít. A webes API-k kicsit gyorsabban változnak, bővülnek, ezért talán könnyebb JSON-ban valamit alkotni, mint egy UML eszközzel nekiesni, mert talán ágyúval verébre és a konkurencia már háromszor megcsinálta, mire az első verzió kijön. Szép a módszertanilag korrekt és alapos fejlesztés, de a webes világ nem ebbe az irányba megy. (Nézd csak meg, a Facebook hányszor rohad le, mégis mindenki meg van őrülve, hogy arra fejleszthet.)

Ezek nyilván itt csak hevenyészve összecsapott érvek, de valamiért a fejlesztői társadalom célszerűbbnek látta a viszonylag egyszerű webfelületekhez JSON-t használni.
28

Ennél árnyaltabb lehet a kép

inf3rno · 2010. Okt. 12. (K), 03.23
Php soap kiterjesztése tényleg hírhedten gagyi, flash-ét nem néztem meg, mert azt zsigerből utálom. A silverlight - soap - asp.net kombinációról mit gondolsz?
(Nem fejlesztettem benne még soha, de gondolom mivel SOAPot ms találta ki, ezért csak össze tudtak hozni egy használható kliens osztályt silverlight-hoz.)


Nekem az a benyomásom erről az egész soap dologról, hogy az olyan divatnyelvek, mint a flash-actionscript vagy a php továbbra is a gagyit támogatják a már kidolgozott szabványok helyett. Végül úgyis ugyanoda fognak eljutni, mint amit soap tud, csak xml alapok helyett json-nal (pl: xml schema helyett lesz ill. már van json schema, stb...). Ennek örülök is, meg nem is. Azért örülök, mert szeretem a javascriptet, és nyilván azért nem, mert minek valamit kétszer feltalálni?


Hogy még egy harmadik dolgot mondjak, SOAP-ból az ember legtöbbször kódot generál, amit aztán nem szívesen módosít. A webes API-k kicsit gyorsabban változnak, bővülnek, ezért talán könnyebb JSON-ban valamit alkotni, mint egy UML eszközzel nekiesni, mert talán ágyúval verébre és a konkurencia már háromszor megcsinálta, mire az első verzió kijön.

Java-t nem régóta tanulom, viszont én úgy tudom, hogy ott nem wsdl-ből lesz a kód, hanem annotációkkal adják meg, hogy mi menjen ki webservice-be, aztán ezek alapján generálják a wsdl fájlt. (Amúgy egy darabig én is az uml->wsdl->php,js módon akartam kódot generáltatni, de pont emiatt a nehéz módosíthatóság miatt vetettem el az ötletet, meg mert lusta voltam megírni az XSLT-ket hozzá :P)
21

Találtam egy az én

inf3rno · 2010. Okt. 10. (V), 22.36
Találtam egy az én elképzeléseimhez közelítő megoldást itt:
http://www.tonymarston.net/php-mysql/model-view-controller.html
(Nem olvastam még el, csak belepörgettem.)
Én annyit módosítanék az ő változatán, hogy kitenném kliens oldalra az XSL transzformációt, szerver oldalon pedig a Controller az WebService lenne.
22

Hogy ne csak beszéljek. Egy

zhagyma · 2010. Okt. 10. (V), 22.50
Hogy ne csak beszéljek. Egy korábbi próbálkozásom: test Techikai demónak mondanám. Ha érdekel a server oldalt elküldöm.

Tuladonképpen xml üzeneteket cserél a böngésző és a szerver. Az XML-ben kapott információt megjeleníti a javascript a böngészőben. Ebből hiányzik még az OO ...
23

Persze, küldd, majd átnézem

inf3rno · 2010. Okt. 11. (H), 12.27
Persze, küldd, majd átnézem valamikor. (Gmail, a felh ugyanaz, mint a skype-nál.)
29

Érdekes a kódod, teljesen

inf3rno · 2010. Okt. 24. (V), 02.44
Érdekes a kódod, teljesen hiányzik belőle az objektum orientáltság. Tényleg csak modulokból áll. Ahogy bonyolódik, úgy kezd kijönni ennek a hátránya, a függvények és a paraméterek nevei km hosszúak a vége fele. :-) Ennek ellenére egész átláthatóra sikerült. Milyen programmal csinálsz dokumentációt?
31

Szándékosan nincs benne OO.

zhagyma · 2010. Okt. 26. (K), 09.00
Szándékosan nincs benne OO. Ezen technológiai demón keresztül próbáltam elsajátítani annak idején a JavaScript, a DOM, a cross-browser támogatás alapjait. A kilométer hosszú teljes hivatkozások eleinte engem is zavartak, de hát a YUI 2.xx is elvolt vele. :-) A YUI lokális változokba tárolt linkeket használ egy függvényen, objektumon belül a probléma megoldására. Én is ebben maradtam ...

Általad és általam is hiányolt OO implemetáció idő hiányában elmaradt. Ezért is olvasom a cikksorozatodat, hátha okosabb leszek ...

Milyen programmal csinálsz dokumentációt?


Nem terveztem a kódban található megjegyzésekből dokumentáció gyártását (egyedi kulcsszavak és jelölések vannak a kommentekben, no meg ECMA / W3C szabványrészletek) mivel tanulási célú tech demoról van szó. A dokumentálás formátuma a Java programozás miatt rámragadt "rossz szokás". Ha esetleg ilyesmit keresel a YUI csinált JavaScriptre egy a Javadoc-hoz hasonló rendszert:
32

Köszi, megnézem, bár lehet,

inf3rno · 2010. Okt. 26. (K), 12.59
Köszi, megnézem, bár lehet, hogy írok saját programot dokumentáció generálásához. Github-on tákolok wiki-t hozzá, az meg elég speciális...
30

Elnézést, hogy csak most

snd · 2010. Okt. 26. (K), 07.50
Elnézést, hogy csak most reagálok!
Nyilván az ember egy konstruktort arra használ, hogy példányosítson vele

És ez így is van. A js-nek van egy olyan sajátossága, hogy egy függvény lehet függvény és konstruktor is. Előző hozzászólásom: Class() vs new Class(). Az előbbi függvényhívás és a visszatárási érték a függvény visszatérési értéke. Míg a második esetben a függvény konstruktorként funkcionál és a visszatérési érték, függetlenül attól, hogy a függvénynek (ez esetben konstruktornak) van-e visszatérési érték beállítva, egy új objektum, mely a prototype-ból örökli a tulajdonságait.
Mire gondolsz?

Mint ahogy írtam is: Főleg a Object-re
Azzal kezdted a cikket, hogy a kiegészítetted az Object-et: Object.prototype.hasOwnProperty, Object.prototype.getClass

// Chromium 6.0.472.62 (59676) Ubuntu 10.04
var a = {"a":0, "b":1}; 
var keys = []; 
for (var k in a){
   keys.push(k); 
} 
console.log(keys); // ["a", "b", "getClass"]
console.log('a:',a); 
// ha a Object.prototype.getClass-hoz nem névtelen függvényt rendelünk, hanem Object_getClass nevet adunk neki
console.log(a.getClass, a.getClass.name); // function() Object_getClass
console.log(a.constructor, a.constructor.name); // Object() Object
Szándékosan nem osztottam meg

Én sajnálom! Engem minden unalmas részlet érdekel. :)

Valamelyik, valakinek írt válaszodból nekem az jött át, hogy az a néhány, a cikkel nem mindenben egyetértő hozzászólás letörte a lelkesedésedet és abba kívánod hagyni. Szerintem ne tedd:
1. Amit én írok az az én teljesen szubjektív véleményem.
2. Mint ahogy valaki írta is: van rá igény, hiszen mennyi hozzászólás született...
3. Én személy szerint örülök, hogy a itt ilyen cikk is van. Olyan ami egy technológia határait feszegeti.
33

Míg a második esetben a

inf3rno · 2010. Okt. 26. (K), 13.20
Míg a második esetben a függvény konstruktorként funkcionál és a visszatérési érték, függetlenül attól, hogy a függvénynek (ez esetben konstruktornak) van-e visszatérési érték beállítva, egy új objektum, mely a prototype-ból örökli a tulajdonságait.

Ez így nem igaz. A konstruktor hívásakor minden esetben létrejön egy új objektum, de ha beállítjuk a visszatérő értéket, akkor nem ezt az objektumot fogjuk kapni. Alapból annyi van js motorban, hogy ha undefined a visszatérő érték, akkor azt állítsa át this-re. Végülis ez is automatizálás valamennyire, hogy ne kelljen kiírni a konstruktorokban, hogy return this.
Ezzel persze lehet vitatkozni, de ha kötelezően az új példányt kellene megkapni, akkor letiltották volna a return használatát a konstruktorokban.

Mint ahogy írtam is: Főleg a Object-re

Nem az Object volt a kérdés, hanem, hogy milyen problémákat okozhat, ha belenyúlok az Object.prototype-ba? (egyébként még párszor meg fogom tenni :-) )

Én sajnálom! Engem minden unalmas részlet érdekel. :)

Idővel megkapod őket. :-)

Valamelyik, valakinek írt válaszodból nekem az jött át, hogy az a néhány, a cikkel nem mindenben egyetértő hozzászólás letörte a lelkesedésedet és abba kívánod hagyni.

Nem csak azzal volt gond, hogy kicsit letört a lelkesedésem, inkább azzal, hogy sok a zh meg a szigorlat ebben a félévben, aztán nem tudom olyan ütemben folytatni a cikkírást, mint szerettem volna. Az első három cikket úgy írtam, hogy két hetente egy cikk, de ez már sajnos nem tartható. Valszeg ezentúl csak havonta vagy ritkábban fogok cikket írni, amíg vége nincs a félévnek.

Szerintem ne tedd

Oké, nem teszem :-)
34

Szerintem te nagyon kevered a

snd · 2010. Okt. 29. (P), 08.01
Szerintem te nagyon kevered a dolgokat, és csak azt látod meg amit meg akarsz látni! Azt hiszem tévedtem: tedd...
35

Jogod van a véleményedhez,

inf3rno · 2010. Okt. 29. (P), 12.37
Jogod van a véleményedhez, nekem meg jogom van nem figyelembe venni azt. :-)
12

Nos az van, hogy ezt a

inf3rno · 2010. Okt. 10. (V), 19.47
Nos az van, hogy ezt a rendszert nem szeretném, és szerintem nem is lehet megcsinálni modulárisan, ezért úgy döntöttem, hogy mivel nincs érdeklődés ezekre a cikkekre, nem írom tovább ezt a cikksorozatot.
14

Félreértés

janoszen · 2010. Okt. 10. (V), 19.54
Miért ne lenne? Attól, hogy jött szembe néhány ellenvélemény, ne add föl. Örülj, hogy sikerült egy olyan témába belenyúlnod, ami sok mindenkinek piszkálja a fantáziáját. Ha nem lenne igény, nem kommentelt volna senki sem.
19

Hát egyelőre mindenképp

inf3rno · 2010. Okt. 10. (V), 22.11
Hát egyelőre mindenképp szünetet tartok ezekkel a cikkekkel, mert nagyon sok a leadandó jegyzőkönyv meg a zh, később majd meglátom, meg elgondolkodok ezen a modularitásos témán is.

Az a baj, hogy csomag, osztály, metódus nevek lekérését, beépített típusellenőrzést meg ilyesmiket lehetetlen modulárisan megoldani. Mindenképp kell egy rendszer, ami ezeket menedzseli.

Mondok egy példát (az eddig vázoltakhoz képest még jobban bővített rendszerre). Vannak ezek az osztályok (lenti kód), a Bean azt jelenti, hogy a types-ban megadott dolgokra automatikusan létrehoz setter és getter metódusokat. A Collections.List olyasmi, mint a java collections framework List-je, ha beállítom az element típusát Car-ra, akkor csak kocsikat fogad el a lista. (Agyaltam azon, hogy hogyan lehetne új osztály (CarList) létrehozása nélkül megadni a listában szereplő típust, de még nem jutottam semmire...)

this.append(
{
	Car: new Bean(
	{
		types:
		{
			licenseNumber: String,
			color: String
		}
	})
});
this.append(
{
	CarList: Collections.List.extend(
	{
		types:
		{
			element: Car
		}
	})
});
this.append(
{
	User: new Bean(
	{
		types:
		{
			cars: CarList,
			name: String,
			email: String,
			password: String,
			nick: String
		}
	})
});
Mondjuk példányosítok egy ilyen osztályt, és beállítok néhány értéket setter-rel.

var user=new User()
	.setCars(new Cars()
		.add(new Car()
			.setLicenseNumber("HIF210")
			.setColor("black")
		)
	)
	.setName("Jánszky László");
vagy éppen kapok JSON-ban egy példányt (itt vegyük észre, hogy az osztály létrehozásánál megadott típusokra automatikusan konvertálja a JSON-t, a hagyományos megoldások ezzel szemben csak kézzel tudják megoldani mindezt):

var user=User.extract(
{
	cars:
	[
		{licenseNumber:"HIF210",color:"black"}
	],
	name:"Jánszky László"
});
vagy mondjuk egy ilyen példánynál lekérem az első kocsi rendszámát getter-rel:

var firstCarLicense=user.getCars().firstElement().getLicenseNumber();
vagy ugyanez xpath-el:

var firstCarLicense=user.xpath("cars/*[1]/licenseNumber");
Az ilyen dolgokat hogyan csináljam meg modulárisan? :-) hogy tákoljam bele Function.prototype-ba? Vannak olyan kaliberű dolgok, amiket már nem lehet egy 5 soros függvényben megírni... Én nem értek azzal egyet, hogy modulárisra kéne csinálnom a rendszert, és hogy ne válasszam el a függvényeket az osztályoktól...

Az, hogy túlzottan nagy a rendszer js-hez más kérdés. Én úgy gondolom, hogy megéri, mert sokkal gyorsabban lehet fejleszteni egy ilyen rendszerben, mint core javascriptben, vagy minimálisan kibővített javascriptben, továbbá az automatizálásnak még megvan az az előnye is, hogy csökkenti a hibázási lehetőségek számát.
Sebességben nem feltétlen annyira jók ezek a megoldások, mint egyszerűbb keretrendszereknél, de ha az én 7 éves gépem gond nélkül viszi őket, akkor szerintem máshol sem fognak gondot okozni. Ugye a javascript motorokat meg amúgy is egyre gyorsabbra csinálják a böngésző gyártók...
16

Csatlakozom

Török Gábor · 2010. Okt. 10. (V), 21.25
Osztom proclub véleményét, nagyon érdekes és tanulságos a megkezdett cikksorozatod. Mindenannyiunk okulására támogatom a sorozat folytatását.
24

Rendben, akkor folytatom a

inf3rno · 2010. Okt. 11. (H), 12.39
Rendben, akkor folytatom a sorozatot.
(Az öröklődéses részt most hétvégén átírom, az azt követő rész csak november elejére-közepére esedékes.)