molily: die Hintergründe erklärt: Methoden, Handler und Closures

Beitrag lesen

Hallo,

generell solltest du dir http://aktuell.de.selfhtml.org/artikel/javascript/organisation/ durchlesen - auch wenn es da nicht um künstliche Events geht.

Das hier sollte dir klar sein:

function Konstruktor () {  
 alert("Zugriff auf das neu erschaffene Objekt: " + this);  
 this.öffentlicheMethode = function () {  
  alert("Zugriff auf das neu erschaffene Objekt: " + this);  
 };  
}  
var meinObjekt = new Konstruktor;  
meinObjekt.öffentlicheMethode();

In Objektmethoden zeigt this auf die jeweilige Instanz. Man sagt, die Objektmethoden werden im Kontext der Instanz ausgeführt - das heißt eben, das this darauf zeigt. Nur darin zeigt sich die Zugehörigkeit einer Funktion zu einem Objekt; das macht die Methode zur Methode.

Bei Event-Handlern kommt jetzt eine andere Sache hinein: In Handlerfunktionen zeigt »this« auf das Objekt, bei dem der Event passierte, also dessen Handler gerade ausgelöst wurde.

document.body.addEventListener("click", handler, false);  
function handler () {  
 alert("Kontext: " + this);  
}

Hier ist this gleich das DOM-Knotenobjekt (in dem Fall Elementobjekt), bei dem der click-Event passierte.

Wenn du nun mit synthetischen Events arbeitest, passiert eigentlich genau dasselbe: this zeigt in der Handlerfunktion auf das DOM-Knotenobjekt bzw. ein sonstiges Objekt wie z.B. window, bei dem du das Ereignis mit dispatch eingeleitet hast.

window.onload = start;  
function überwache (e) {  
 alert(e.target + "\n" + e.currentTarget + "\n" + this);  
}  
function start () {  
 document.body.addEventListener("MeinEreignis", überwache, true);  
 var ev = document.createEvent("Events");  
 ev.initEvent("MeinEreignis", true, true);  
 document.body.dispatchEvent(ev);  
}

target, currentTarget und this sind hier identisch. (this ist immer identisch mit currentTarget, wegen aufsteigender Events nicht notwendigerweise identisch mit target. Hier wird der Event aber genau dort behandelt, wo er auch ursprünglich passiert - bei document.body.)

Soweit klar - das Problem entsteht jetzt bei der verschachtelten Notierung von Objektmethoden und Event-Handler-Funktionen.

Mal die obigen Beispiele kombiniert:

function Konstruktor () {  
 this.starteÜberwachung = function () {  
  var überwache = function (e) {  
   alert(e.target + "\n" + e.currentTarget + "\n" + this);  
  };  
  document.body.addEventListener("MeinEreignis", überwache, true);  
 };  
 this.löseAus = function () {  
  var ev = document.createEvent("Events");  
  ev.initEvent("MeinEreignis", true, true);  
  document.body.dispatchEvent(ev);  
 };  
}  
  
window.onload = start;  
  
function start () {  
 var meinObjekt = new Konstruktor;  
 meinObjekt.starteÜberwachung();  
 meinObjekt.löseAus();  
};

Jetzt musst du dir nur klar darüber werden, in welchem Kontext die Funktionen jeweils ausgeführt werden.

  • starteÜberwachung wird im Kontext von meinObjekt ausgeführt.
  • überwache wird zwar in einer Methode von meinObjekt notiert, wird aber dann als Handlerfunktion an document.body gebunden. In dessen Kontext wird diese Funktion schließlich auch ausgeführt! Also zeigt this darin nicht auf meinObjekt, sondern document.body.
  • löseAus wird wieder normal im Kontext von meinObjekt ausgeführt.

Wie kommt man in überwache nun elegant an die Instanz heran (also meinObjekt)? Da gibt es verschiedene Möglichkeiten (siehe Artikel), von denen du eine auch letztlich selbst gefunden hast, wenn womöglich auch unabsichtlich: nämlich Closures. Die wurden die auch empfohlen, als von »var self = this« die Rede war.

Closures sind eine Eigenart, die bei verschachtelten Funktionen auftritt: Die lokalen Variablen der äußeren Funktion sind auch in der inneren verfügbar, selbst wenn diese zu einer anderen Zeit in einem anderen Kontext auseführt wird.

function Konstruktor () {  
 var self = this;  
 this.eigenschaft = "bla";  
 this.starteÜberwachung = function () {  
  // Funktion in Funktion: Closure, die self einschließt  
  var überwache = function (e) {  
   // Funktion in Funktion in Funktion: Closure, die self einschließt  
   alert(self + " versus " + this);  
   alert(this.eigenschaft);  
  };  
...

So würde ich es lösen - alle Variablen als Objekteigenschaften lösen, dann *eine* Variable (nämlich self) weitervererben, die auf die Instanz verweist. Darüber dann auf die Objekteigenschaften zugreifen. Du hast dich dazu entschlossen, viele Variablen durch Closures weiterzureichen (Count, Request, ResponseHandler, Event, Handler, Index).

Mathias