Andree Filchos: [oop] this bei Funktionsaufruf durch Eventhandler

Hallo,

ich habe eine eigene Klasse test mit einer Eigenschaft foo und einer Funktion showFoo. Letztere soll durch ein Event getriggert werden und zeigt hier lediglich ein paar Werte an.
In dem folgenden Beispielcode (hier stark verkürzt) wird eine Instanz myTest erzeugt. Die Funktion showFoo dieser Instanz kann auf zwei Weisen aufgerufen werden:

Achtung: Dieser Beispielcode läuft nur unter Mozilla.

  1. Ich klicke den Link. Richtigerweise zeigt die Funktion e als undefiniert an und gibt mir in this.foo den Wert "bar" zurück.

  2. Ich mache einen Rechtsclick. Der Eventhandler ruft die Funktion auf und zeigt e richtig als [Object Event] an. In this allerdings finde ich nicht die erwartete Instanz, sondern ein [Object HTMLDocument], dessen nodeName #document ist. this.foo ist daraus folgend undefiniert.

Frage 1: Woher kommt diese Referenz in this auf ein HTMLDocument-Object? Bug oder Feature?
Frage 2: Wenn dieser Weg nicht gangbar ist, wie kann ich auf die Instanz und damit zum Beispiel auf die Eigenschaft foo zurückgreifen?

Ich freue mich über alle kompetenten Antworten
Gruß Andree

-----
Der Beispielcode:

<html>
  <head>
    <script>

function test() {
        this.foo = "bar";
        this.showFoo = function (e) {
          alert("Event:" + e + "\nthis:" + this + "\nFoo: " + this.foo);
        }
      }

</script>
  </head>
  <body>
    <a href="#" onclick="myTest.showFoo();">Show Foo</a>

<script>
      myTest = new test();
      document.oncontextmenu = myTest.showFoo;
    </script>
  </body>
</html>

  1. Привет Andree.

    wie kann ich auf die Instanz und damit zum Beispiel auf die Eigenschaft foo zurückgreifen?

    Wenn du ein Event "umbiegen" willst, musst du einen Handler angeben, so wie du es wahrscheinlich auch vorhattest. M.E. ist der von dir hier:

    document.oncontextmenu = myTest.showFoo;

    angegebene Code der Knackpunkt, da du auf eine Methode des Objektes test referenzierst, nicht allgemein auf eine Funktion. Wenn man das so ändert:

    document.oncontextmenu = initFoo;

    und im Head noch folgende Funktion notieren:

    function initFoo() {
      obj = new test();
      obj.showFoo();
    }

    dann funktioniert es.

    Дружба!
    Siechfred

    --
    »Sie kochten heimlich mit Wasser und tranken öffentlich Wein.«
    1. Hallo Siechfred,

      vielen Dank für Deine Antwort. Damit bin ich schon ein ganzes Stück weiter.

      In dem wirklichen Programm möchte ich eigentlich durch den eventhandler nicht die Funktion showFoo() einer beliebigen Instanz von test aufrufen, sondern gerade auf die Instanz myTest zugreifen.

      Als workaround habe ich jetzt die Funktion

      function wrapperShowFoo() {
        myTest.showFoo();
      }

      implementiert, die global (unfein!) auf die Instanz myTest zugreift. Sie wird durch

      document.oncontextmenu = wrapperShowFoo;

      aufgerufen. Etwas besseres fällt mir leider nicht ein.

      Falls Du eine sauberere Lösung hast, würde ich mich über eine Antwort natürlich freuen.

      Gruß Andree (übrigens kein Russe - Andree ist einfach Spitzname)

      1. Korrektur:

        Damit das Eventobjekt weitergegeben wird, lautet die Funktion:

        function wrapperShowFoo(e) {
          myTest.showFoo(e);
        }

        Gruß Andree

        1. Привет Andree.

          Ich habe noch was gefunden:
          http://javascript.jstruebig.de/exp/events.html.

          Дружба!
          Siechfred

          --
          »Sie kochten heimlich mit Wasser und tranken öffentlich Wein.«
      2. Привет Andree.

        In dem wirklichen Programm möchte ich eigentlich durch den eventhandler nicht die Funktion showFoo() einer beliebigen Instanz von test aufrufen, sondern gerade auf die Instanz myTest zugreifen.

        Ich habe mir das noch mal angeschaut, du könntest es auch über eine anonyme (namenlose) Funktion versuchen zu lösen (Struppi hatte da IIRC mal was zu gepostet):

        document.oncontextmenu = function() { myTest.showFoo() };

        Ach ja, wegen des this-Problems. Wenn du dem Eventhandler eine Referenz auf die Funktion showFoo zuweist, bezieht sich m.E. "this" auf das Element, für welches du das Eventhandling auf diese Funktion umgebogen hast. Und das war bei dir "document", somit ist es wohl weder Bug noch Feature, sondern ein Denkfehler deinerseits :)

        Дружба!
        Siechfred

        --
        »Sie kochten heimlich mit Wasser und tranken öffentlich Wein.«
  2. Achtung: Dieser Beispielcode läuft nur unter Mozilla.

    1. Ich klicke den Link. Richtigerweise zeigt die Funktion e als undefiniert an und gibt mir in this.foo den Wert "bar" zurück.

    Du kannst im HTML Tag den event auch übergeben.
    <a href="#" onclick="myTest.showFoo(event);">Show Foo</a>
    (nur als Info)

    1. Ich mache einen Rechtsclick. Der Eventhandler ruft die Funktion auf und zeigt e richtig als [Object Event] an. In this allerdings finde ich nicht die erwartete Instanz, sondern ein [Object HTMLDocument], dessen nodeName #document ist. this.foo ist daraus folgend undefiniert.

    this im Event ist die Referenz auf das Objekt.

    [Object].event()
    {
       this == [Object]
    }

    Frage 1: Woher kommt diese Referenz in this auf ein HTMLDocument-Object? Bug oder Feature?

    s.o.

    Frage 2: Wenn dieser Weg nicht gangbar ist, wie kann ich auf die Instanz und damit zum Beispiel auf die Eigenschaft foo zurückgreifen?

    Wie Siechfried schon sagte, mit einer anonymen Funktion:

    myTest = new test();
    document.oncontextmenu = function(e)
    {
    myTest.showFoo(e);
    }

    Problematisch wird es wenn du im Objekt den Eventhandler zufügen willst:

    <a href="#" id="x">Show Foo</a>

    <script>
    function test(id)
    {
        this.obj = document.getElementById(id);
        if(!this.obj) return;
        this.foo = "bar";
        this.obj.onclick = function (e)
        {
              alert("Event:" + e + "\nthis:" + this + "\nFoo: " + this.foo);
              return false;
        }
    }

    myTest = new test('x');

    </script>

    Hier hast du erstmal keinen Zugriff auf das äußere this und musst dem Objekt das aktuelle Objekt übergeben (es gibt sicher auch andere Lösungen, mir ist aber noch keine bessere eingefallen):

    ...
        this.foo = "bar";
        this.obj.parent = this;
        this.obj.onclick = function (e)
        {
              alert("Event:" + e + "\nthis:" + this + "\nFoo: " + this.parent.foo);
              return false;
        }

    HTH
    Struppi.