gruss Don P,
Du skizzierst gerade probleme und loesungsansaetze, die mit folgenden
konzepten hinreichend genug abgedeckt sind:
deutsche Wikipedia:
englische Wikipedia:
- [http://en.wikipedia.org/wiki/Observer_pattern@title=»Observer Pattern«]
- [http://en.wikipedia.org/wiki/Publish/subscribe@title=»publish-subscribe«]
- [http://en.wikipedia.org/wiki/Signals_and_slots@title=»Signals and Slots«]
Du benoetigst also keineswegs »eigene DOM-Events«, sondern nur eine saubere
und vernuenftig bedienbare umsetzung eines wie auch immer gearteten beobachter-
entwurfsmusters.
»Signals and Slots« und JavaScript, und für letzteres besonders die art
und weise seines »event«-getriebenen (ereignisse der benutzeroberflaeche
bzw. client/server-request/response-pingpong) einsatzes, sind fuereinander
wie geschaffen.
das sprachkonzept von JavaScript wiederum laesst implementierungen von
Mixins/Traits/Behaviors/Roles (sucht Euch das wort raus, welches Euch am
meisten zusagt) zu, so dass eine in meinen augen saubere und typsichere
implementierung von »Signals and Slots« in der lage ist, jedem nativen
JavaScript-objekt eine art [EventTarget]-schnittstelle zu verbacken, die
diese objekte um die eigenschaften [addEventListener], [removeEventListener]
und [dispatchEvent] anreichert.
die gerade herbeitheoretisierte implementierung von »Signals and Slots«
bekommt jetzt den konkreten namen [EventDispatcher]. [EventDispatcher] ist
ein JavaScript-Singleton, welches intern konstruktoren fuer [EventTarget],
[EventListener] und [Event] kapselt.
jedes objekt »obj«, welches sich dort mit »EventDispatcher.register(obj)«
anmeldet, hat sofort [EventTarget]-verhalten, ist also umgehend in der
lage, eigene events zu werfen und registrierungen bzw. abmeldungen fuer
die zu diesen events gehoerenden event-listener entgegenzunehmen.
das fuktioniert bereits problemlos auf bassis jeder instanz aller in
JavScript eingebauten kernobjekte ("build in" objects) - ist aber noch
ohne wirklich praktischen nutzen.
wirklich interessant wird die ganze geschichte vor dem hintergrund
selbstgetrickter objekttypen (konstruktor-funktionen) bzw. eigener
Mixins/Traits/Behaviors/Roles (»funktionen als implementierte interfaces?«).
einfaches sofort in der [jconsole] ueberpruefbares bsp.:
//[http://dean.edwards.name/packer/] - shrinked - 2945 bytes :
var EventDispatcher=(function(){var g=Object.prototype.toString,stringify=(function(a){return((a&&a.toString&&a.toString())||"")}),isBoolean=(function(){var b=(/^\[object\s+Boolean\]$/);return(function(a){return b.test(g.call(a))})})(),isString=(function(){var b=(/^\[object\s+String\]$/);return(function(a){return b.test(g.call(a))})})(),isFunction=(function(a){return((typeof a=="function")&&(typeof a.call=="function")&&(typeof a.apply=="function"))}),Handler={indexOf:(function(a,b){var c=(a.length-1);while(c>-1){if(a[c]===b){break}--c}return c})},Event=(function(a,b){this.constructor=Event;this.target=a;this.type=b;this.timeStamp=new Date()}),EventListener=(function(b,c,d){this.constructor=EventListener;var e=new Event(b,c);this.handleEvent=(function(a){if((typeof a=="object")&&a){a.target=e.target;a.type=e.type;a.timeStamp=e.timeStamp}else{a={target:e.target,type:e.type,timeStamp:e.timeStamp}}d(a)});this.getType=(function(){return c});this.getHandler=(function(){return d})}),EventTarget=(function(){var f={},removeEventListener=(function(a,b){var c=f[a],successfully=false;if(c){var d=c.handlers,listeners=c.listeners,idx=Handler.indexOf(d,b);if(idx>=0){d.splice(idx,1);listeners.splice(idx,1);successfully=true}}return successfully});this.addEventListener=(function(a,b){var c;if(a&&isString(a)&&isFunction(b)){var d=f[a],listener=new EventListener(this,a,b);if(d){var e=d.handlers,listeners=d.listeners,idx=Handler.indexOf(e,b);if(idx==-1){e.push(listener.getHandler());listeners.push(listener);c=listener}else{c=listeners[idx]}}else{d=f[a]={};d.handlers=[listener.getHandler()];d.listeners=[listener];c=listener}}return c});this.removeEventListener=(function(a,b){return((isString(a)&&isFunction(b)&&removeEventListener(a,b))||((a instanceof EventListener)&&removeEventListener(a.getType(),a.getHandler()))||false)});this.dispatchEvent=(function(a){var b=false,type=(((typeof a=="object")&&(typeof a.type=="string")&&a.type)||((typeof a=="string")&&a)),event=(type&&f[type]);if(event){var c=(event&&event.listeners),len=((c&&c.length)||0),idx=0;if(len>=1){while(idx<len){c[idx++].handleEvent(a)}b=true}}return b})}),pseudoTarget=new EventTarget,CROSSCHECK_TARGET={addListenerString:pseudoTarget.addEventListener.toString(),removeListenerString:pseudoTarget.removeEventListener.toString(),dispatchEventString:pseudoTarget.dispatchEvent.toString()};delete pseudoTarget;return{register:(function(a){if((typeof a.addEventListener=="function")||a.attachEvent||(typeof a.removeEventListener=="function")||a.detachEvent||(typeof a.dispatchEvent=="function")||a.fireEvent){return}EventTarget.call(a)}),unsubscribe:(function(a){if(stringify(a.addEventListener)===CROSSCHECK_TARGET.addListenerString){delete a.addEventListener}if(stringify(a.removeEventListener)===CROSSCHECK_TARGET.removeListenerString){delete a.removeEventListener}if(stringify(a.dispatchEvent)===CROSSCHECK_TARGET.dispatchEventString){delete a.dispatchEvent}})}})();
var Queue = (function () { // [http://de.wikipedia.org/wiki/Datenstruktur#Warteschlange]
EventDispatcher.register(this); // applying the [EventTarget] mixin/behavior/interface.
var self = this, list = [],
onEnqueue = (function (obj) {
//self.dispatchEvent({target: self, type: "onEnqueue", elm: obj/*, even more key:value pairs */});
self.dispatchEvent({type: "onEnqueue", elm: obj});
//self.dispatchEvent("onEnqueue");
}),
onDequeue = (function (obj) {
//self.dispatchEvent({target: self, type: "onDequeue", elm: obj/*, even more key:value pairs */});
self.dispatchEvent({type: "onDequeue", elm: obj});
//self.dispatchEvent("onDequeue");
}),
onEmpty = (function () {
//self.dispatchEvent({target: self, type: "onEmpty"/*, even more key:value pairs */});
//self.dispatchEvent({type: "onEmpty"/*, even more key:value pairs */});
self.dispatchEvent("onEmpty");
});
this.constructor = arguments.callee;
this.enqueue = (function (obj/*:[Object]*/) { /* enqueue | line up | [Array.push] */
list.push(obj);
this.length = list.length;
onEnqueue(obj);
});
this.dequeue = (function () { /* dequeue | line up | [Array.shift] */
var obj = list.shift();
if (list.length === 0) {
onEmpty();
}
onDequeue(obj);
return obj;
});
this.length = 0;
});
var myQueue = new Queue;
myQueue.addEventListener("onEnqueue", (function (evt) {print("onEnqueue - evt : " + evt + " , evt.target : " + evt.target + " , evt.type : " + evt.type + " , evt.elm : " + evt.elm)}));myQueue.addEventListener("onDequeue", (function (evt) {print("onDequeue - evt : " + evt + " , evt.target : " + evt.target + " , evt.type : " + evt.type + " , evt.elm : " + evt.elm)}));
myQueue.addEventListener("onEmpty", (function (evt) {print("onEmpty - evt : " + evt + " , evt.target : " + evt.target + " , evt.type : " + evt.type + " , evt.elm : " + evt.elm)}));
print("\n");
print("\n");
myQueue.dequeue();
print("\n");
print("\n");
myQueue.enqueue("Signals");
print("\n");
myQueue.enqueue("and");
print("\n");
myQueue.enqueue("Slots");
print("\n");
print("\n");
myQueue.dequeue();
print("\n");
myQueue.dequeue();
print("\n");
myQueue.dequeue();
print("\n");
ende - teil 1 ... teil 2 wird hier gleich im anschluss gepostet ...