dedlfix: Aufrufende Variable im Objekt ermitteln

Beitrag lesen

Tach!

mit button.addEventlistener('click',this.closeWindow) geht's auch nicht, weil sich "this" in dem Fall auf den Button und nicht auf das Objekt bezieht

Das this bezieht sich nur dann auf den Button wenn der Aufrufkontext, in dem der Eventhandler gesetzt wird, eine Methode des Buttons ist. Dann könnte man aber auch gleich this.addEventListener… schreiben. Wenn der Aufrufkontext eine Methode des Windows ist, dann zeigt this auf das Window.

Das Problem ist aber, dass zum Zeitpunkt des Events der Kontext ein anderer ist, und ein im Eventhandler verwendetes this auf den Kontext zum Zeitpunkt des Ereignisses verweist und nicht mehr auf das Window. Die Methode wird zwar aufgerufen, aber so, als würde man sie statisch aufrufen, und nicht als Methode eines konkreten Window-Objekts.

warum speicherst du das gewünschte „this“ nicht:

dieses = this;
Button.addEventlistener('click',dieses.closeWindow);

Damit löst man das Problem nicht. Der Eventhandler bindet sozusagen nur die Adresse der Methode, aber es wird keine Closure erzeugt, die irgendwelche Daten aus dem derzeitigen Aufrufkontext festhalten würde, auf die man zum späteren Zeitpunkt des Events zugreifen könnte. Deshalb ist es egal, ob man das this direkt schreibt oder eine Indirektion über eine weitere Variable verwendet.

Wenn sich allerdings closeWindow im selben Scope befindet, dann bildet dieser Scope die Closure und man kann in closeWindow auf dieses zugreifen. Aber besser ist, den Aufruf der Arrow-Funktion aus dem nachfolgenden Beispiel zu verwenden, dann kann closeWindow auch sonstwo definiert sein und man braucht keine Hilfsvariable.

Nehmen wir folgendes Beispiel.

<button id="foo" data-x=42>foo</button>
<button id="bar" data-x=23>bar</button>

<script>
const foo = document.querySelector('#foo');
const bar = document.querySelector('#bar');

console.log(foo.dataset);
console.log(bar.dataset);

foo.clickHandler = function() {
    console.log('click', this.dataset);
    bar.addEventListener('click', this.clickHandler);
}
foo.addEventListener('click', foo.clickHandler);

</script>

Zwei Buttons, jeder mit einem eigenen Wert in data-x, der zur Kontrolle jeweils mit einem console.log() ausgegeben wird. Danach geben wir dem foo-Button eine neue Eigenschaft namens clickHandler mit einer Methode als Inhalt. Und ich füge einen Eventhandler für sein Click-Ereignis hinzu. Diese Zuweisung befindet sich im globalen Kontext, this verweist auf das globale window-Objekt, deshalb muss ich foo.clickHandler übergeben.

Wenn wir auf den foo-Button klicken, ruft es den clickHandler auf. Der Event-Mechanismus sorgt dafür, dass this auf den foo-Button zeigt, und console.log() zeigt die 42 von foo.

Als zweite Anweisung wird dem click-Event von bar ein Eventhandler zugewiesen. Das this zeigt noch immer auf foo, so wie es uns der Eventhandlermechanismus übergeben hat. Klicken wir nun auf den bar-Button, wird jetzt ebenfalls diese Methode aufgerufen. Aber die Ausgabe ist nun die 23 von bar. Wie auch schon zuvor wurde vom Eventmechanismus das this auf den Auslöser gesetzt, der nun bar ist. Uns interessiert nur die console.log()-Ausgabe, die Zeile mit dem bar.addEventListener ignorieren wir mal.

Aber für den nächsten Versuch ändern wir sie zu

    bar.addEventListener('click', () => this.clickHandler());

und klicken nacheinander auf foo und bar. Beide Male wird 42 ausgegeben. Hier kommt eine Besonderheit der Arrow-Funktion zum tragen, denn diese bindet in this den Kontext zum aktuellen Zeitpunkt, also einen Verweis auf foo. Im Eventhandler wird die Methode clickHandler() von foo im Kontext von foo aufgerufen, ohne dass wir zum Eventzeitpunkt wissen müssen, dass wir uns in der Instanz von foo befinden. Wir verwenden einfach this.

Ändern wir zum Spaß noch die Zeile in einen Aufruf einer herkömmlichen anonymen Funktion

    bar.addEventListener('click', function() { this.clickHandler(); });

bekommen wir eine Fehlermeldung beim Klick auf bar (und vorher foo)

this.clickHandler is not a function

denn nun wird als this der Button bar durchgereicht, der keine Methode namens clickHandler hat.

Fazit: Arrow-Funktion verwenden. Oder was mit .bind() zaubern, wenn modernes Javascript nicht möglich ist.

dedlfix.