Objekte, die sich selber untersuchen
Bei jedem Aufruf der Funktion wird dieses Argument - sozusagen hinter den Kulissen - zusammen mit einem Zeiger auf das Objekt, das die Funktion aufrief, übergeben. this hat eine ähnliche Funktion wie das »sie« in diesem Satz: »Martina stand früh auf, denn sie wollte Kurts Ankunft nicht verpassen«. Durch »sie« ist klar, dass die zuerst erwähnte Martina gemeint ist.
In objektorientierten Sprachen brauchen wir etwas, um auf das aktuelle Objekt zuzugreifen, das gerade vom Script behandelt wird. Dank Javascript this können sich Objekte selbst untersuchen und Auskunft über ihre Eigenschaften geben.
this is a moving target / this ist ein bewegliches Ziel
this hat keinen Wert, bis ein Objekt die Funktion aufruft, in der this definiert ist.
Javascript this gibt es in jedem Funktions- oder globalen Umfeld. Aber in jedem Umfeld hat this eine andere Zuordnung – ist an ein anderes Element gebunden.
Der Wert von this innerhalb einer Funktion hängt von dem Objekt ab, das die Funktion aufgerufen hat. Im Bild oben ist this in function b einmal das div-Element, auf dem die Funktion aufgerufen wurde, und ein anderes this, wenn die Funktion b auf dem form-Element aufgerufen wurde. Für Einsteiger immer wieder ein Mysterium …
Javascript ist eine spätbindende (late binding) Programmiersprache. Verglichen mit anderen Programmiersprache bindet Javascript sein this sogar außerordentlich spät. this wird nicht während der Compilierung gebunden, auch nicht zur Laufzeit wie in anderen Programmiersprachen, sondern zum Zeitpunkt des Aufrufs. Das verstört selbst erfahrene Programmierer, die von objektorientierten Programmiersprachen kommen.
Einordnung von this
Der Wert von this ist abhängig von Strict Mode, Methodenaufruf, Event Handler oder Arrow Function.
Das sind die typischen Fälle:
- Global / im Strict Mode
- Im Browser → window oder undefined (strict).
- In einer Methode
- obj.method() → this zeigt auf obj.
- Als normale Funktion
- func() → this = global (oder undefined im strict mode).
- Im Event-Handler
- element.onclick = function() { console.log(this) } → zeigt auf das DOM-Element.
- In Arrow Functions
- Besonderheit: this wird lexikalisch übernommen (kein eigenes this).
this bei events auf HTML-Elementen
Das mag kein Beispiel für gutes Javascript sein, denn das ist altmodisches inline-Javascript. Aber nichts könnte Javascript this besser in Szene setzen:
<div onmouseover="this.innerHTML='Ich bin jetzt this!'"></div>
Wenn die Maus über das HTML div hovert, soll dieses div den Text Ich bin jetzt this zeigen.
Javascript this bei Funktionen
Und einen Schritt weiter: das Javascript this in einer Funktion
<div>
<button id="but">Klick!</button>
</div>
…
<script>
if (document.getElementById('but')) {
const button = document.getElementById('but');
button.onclick = function() {
this.innerHTML = "ICH BIN THIS!";
}
}
</script>
In diesem Fall ist der Eigentümer oder Aufrufer der Funktion this.
Was ist dieses this?
this ist ein bewegliches Ziel. Ein paar handfeste Regeln:
-
Außerhalb von allen Funktionen verweist this auf das globale Objekt. In einem Browser ist das traditionell das Window-Objekt.
console.log ("this.location " + this.location);
gibt die URL der aktuelle Seite aus – ein Element des Window-Objekts.
-
In einem Event Handler (z.B. einem onclick) verweist this auf das DOM-Element, auf dem das Ereignis ausgelöst wurde.
document.getElementById ('foo').onclick = function () { alert (this.nodeName); }
-
Wenn eine Funktion als Constructor aufgerufen wird, wird ein neues Objekt erzeugt und this an das Objekt gebunden.
function Foo () { this.bar = 1; } new Foo().bar;
-
Wenn Javascript im Strict-Mode läuft, darf this nicht auf das globale Object (also Window in Browsern) verweisen. Wenn also eine Funktion nicht als Methode eines Objekts aufgerufen wird oder this nicht manuell durch call oder apply an ein Element gebunden wurde, liefert this undefined.
function loo () { return this; } console.log (loo()); // gibt aus: loo *** undefined
this im strict mode
In globalen Funktionen ist this im strict mode also undefined, nicht aber in Constructor-Funktionen.
let person = { vorname: "Penelope", nachname: "Barrymore", vollname: function () { console.log(this.vorname + " " + this.nachname); // Hätte auch so geschrieben werden können: console.log(person.vorname + " " + person.nachname); } }
Würden person.vornamen und person.nachname in der Constructor-Funktion anstelle von this.vorname und this.nachname benutzt, wären die Anweisungen u.U. nicht mehr eindeutig – z.B. wenn eine weitere globale Variable person (von der wir vielleicht nichts wissen) existiert. Derartige Fehler sind nicht einfach aufzufinden, darum wird hier this bevorzugt.
this mit bind neu definieren
call, apply und bind steuern und kontrollieren das Verhalten von this von Funktionen. Jede Funktion hat Zugriff auf .apply(), .bind() und .call(). Diese drei werden auch als »context manipulation methods« bezeichnet, also Methoden zur Kontextsteuerung. Sie steuern den this-Wert von Funktionen und erlauben das Festlegen von Argumenten.
function.bind ()
function.bind() führt die Funktion nicht aus, sondern gibt eine Funktion zurück, die später ausgeführt werden kann – z.B., wenn später ein Event eintritt. So bleibt der Kontext in asynchronen Callbacks und Events erhalten.
bind() ist eine elegante Methode, um einen oder mehrere Parameter an eine Callback-Funktion zu übergeben, ohne dass die Funktion sofort aufgerufen wird. Ein Beispiel für die Anwendung von bind() ist ein addEventListener, der eine EventHandler-Funktion für eine Gruppe von Elementen ausführt, um ein vertikales Akkordeon zu animieren.
<div class="demo"> <div class="box box1 boxAni"></div> <div class="box box2"></div> <div class="box box3"></div> <div class="box box4"></div> </div>
const boxen = document.querySelectorAll(".box");
const handleStackMove = function(elem) {
console.log (this.message)
document.querySelector(".boxAni").classList.remove("boxAni");
elem.classList.add("boxAni");
}
const context = {
message : "Das ist mein Akkordeon"
}
boxen.forEach ((item) => {
item.addEventListener ("click",
handleStackMove.bind(context, item)
)
});
bind() erspart die anonyme Funktion, die wiederum eine Callback-Funktion aufruft, in der Parameter übergeben werden können. bind() bindet das Event mit einer Funktion, die mit dem Schlüsselwort this (erster Parameter) auf das Objekt context verweist. Der zweite Parameter ist das geklickte Element im Akkordeon.
Neben dem einfachen Binden von Funktionen im EventListener sorgt bind() dafür, dass der Wert von this bei asynchronen Aufrufen nicht verloren geht.
function.call
.bind(), wenn eine Funktion später in einem bestimmten Kontext aufgerufen werden soll (z.B. bei Events),
.call() / .apply(), wenn eine Funktion sofort aufgerufen werden soll und den Kontext ändert.
Das einfach Objekt satz1 hat keine Methoden, die Methode mwst hat keine Eigenschaft num, gibt aber this.num zurück.
let satz1 = { num:19 }; let mwst = function (a) { return this.num * a / 100; } mwst.call( satz1, 100);
Das Ergebnis des Aufrufs mwst.call( satz1, 100) ist 19. Die Funktion könnte genauso für ein zweites Objekt satz2 = { num:11 } aufgerufen werden.
19
Wenn die Funktion mehrere Parameter hat, werden sie allesamt im call-Aufruf aufgeführt.
let obj = { num: 2 }; let sum = function (a, b, c) { return this.num + a + b + c; } sum.call( obj, 4, 6, 8);
Das Ergebnis wäre wie erwartet
20
Ein call-Aufruf mit einem Objekt, das mehrere Elemente hat:
let obj = { a: 2, b: 8 }; let sum = function (c) { console.log (this.a + this.b + c); } sum.call ( obj, 3);
13
function.apply
Anstelle der drei Parameter könnte ein Array übergeben werden, dann wäre der Aufruf apply anstelle von call.
let obj = { num:2 }; let arr = [3, 5, 9]; let add = function (a, b, c) { //console.log ("apply " + this.num + a + b + c); } add.apply( obj, arr);
call, apply, bind – welche Methode für welchen Fall?
Fall | Lösung |
---|---|
Sofort aufrufen, this ändern |
call() oder apply() |
Sofort aufrufen, Argumente als Array | apply() |
Funktion später mit festem this nutzen |
bind() |
this in Event-Handlern beibehalten |
bind() |
Mit dem Spread-Operator (...) kann apply() oft durch call() ersetzt werden:
Math.max.call(null, ...numbers); // Statt apply()
apply() wird dadurch im modernen Scripts seltener verwendet.
const myFlowers = { name : "Hortensie", getName : function () { return console.log (this.name); } }; myFlowers.getName();
const myFlowers = { name : "Hortensie", getName : function () { return console.log (this.name); } }; const moreFlowers = myFlowers.getName.bind ({"Nelke"}); moreFlowers ();