JavaScript Function bind()
Funktionsbindung in JavaScript: warum Methoden `this` verlieren, wie func.bind() es fixiert, partielle Anwendung und bind vs. Pfeilfunktionen.
In JavaScript wird der Wert von this innerhalb einer Methode beim Aufruf der Funktion festgelegt, nicht beim Schreiben. Sobald man eine Methode von ihrem object löst — indem man sie als Callback oder an setTimeout übergibt — vergisst sie, zu welchem object sie gehörte. Funktionsbindung ist die Lösung: Sie sperrt this dauerhaft an ein gewähltes object, sodass die Methode immer gleich verhält, egal wo sie aufgerufen wird.
Dieses Kapitel behandelt, wann und warum this verloren geht, wie func.bind() das Problem löst, wie man bind für partielle Anwendung nutzt und wie bind im Vergleich zu Pfeilfunktionen steht.
Das Problem: "this" verlieren
Eine Methode funktioniert einwandfrei, wenn sie als object.method() aufgerufen wird, weil this auf alles gesetzt wird, was links vom Punkt steht. Übergibt man dieselbe Methode jedoch anderswo, ist der Punkt weg — und damit auch this.
Der zweite Aufruf gibt Hello, undefined! aus, weil runLater den callback() als normale Funktion aufruft — ohne object vor dem Punkt, sodass this nicht mehr user ist. (Im Strict Mode ist this gleich undefined, und der Zugriff auf this.firstName würde einen TypeError auslösen.)
Das Gleiche passiert mit setTimeout — es speichert die Funktion und ruft sie später eigenständig auf, ohne ein object anzuhängen:
Mehr darüber, wie this innerhalb von Methoden aufgelöst wird, erfährt man unter Objekt-Methoden, „this".
Lösung mit func.bind()
Die Methode func.bind(thisArg) gibt eine neue Funktion zurück, deren this dauerhaft auf thisArg gesetzt ist. Die ursprüngliche Funktion wird nicht verändert — man erhält eine gebundene Kopie.
Ein gängiges Muster ist es, die gebundene Version wieder am object zu speichern, damit jede Referenz sicher weitergegeben werden kann:
Bindung findet nur einmal statt
Sobald eine Funktion gebunden ist, ist this dauerhaft festgelegt. Erneutes Aufrufen von bind auf dem Ergebnis hat keine Wirkung — die zweite Bindung wird ignoriert.
Partielle Anwendung: Argumente vorbelegen
bind akzeptiert nach thisArg weitere Argumente, die der ursprünglichen Funktion vor allen späteren Argumenten übergeben werden. So lässt sich aus einer allgemeinen Funktion eine spezialisierte erstellen — das nennt sich partielle Anwendung.
Man kann auch this und Argumente zusammen vorbelegen:
bind vs. Pfeilfunktionen
Eine Pfeilfunktion besitzt kein eigenes this — sie übernimmt this aus dem umgebenden Gültigkeitsbereich zum Zeitpunkt ihrer Definition (lexikalisches this). Damit sind Pfeilfunktionen eine leichtgewichtige Alternative zu bind, wenn man das äußere this innerhalb eines Callbacks behalten möchte:
Was man wählen sollte:
- Verwende eine Pfeilfunktion, um das aktuelle
thisinline zu erfassen, besonders in Callbacks, bei denen keine separat wiederverwendbare Funktion benötigt wird. - Verwende
bind, wenn man eine eigenständige Funktion mit einem festenthisbraucht, die anderswo übergeben werden soll, oder wenn man auch Argumente vorbelegen möchte. Im Gegensatz zu einer Pfeilfunktion kann eine gebundene Funktion gespeichert, wiederverwendet und namentlich übergeben werden.
Ein Vorbehalt: Eine Pfeilfunktion kann nicht als Objekt-Methode verwendet werden, bei der this auf das object zeigen soll, da sie this aus dem umgebenden Gültigkeitsbereich (oft dem globalen object) übernimmt, anstatt vom Aufrufer.
Wann setzt man das ein?
- Übergabe einer Methode an einen Event-Handler,
setTimeout/setInterval,Promise.thenoder array-Methoden wieforEach. - Erstellen spezialisierter Funktionen aus allgemeinen mit partieller Anwendung (z. B.
bind(null, presetArg)). - Immer dann, wenn eine Methode auch nach dem Loslösen von ihrem object korrekt funktionieren muss.
Wenn man eine Funktion mit einem bestimmten this sofort aufrufen möchte (anstatt eine wiederverwendbare Kopie zu erstellen), verwendet man stattdessen call/apply — siehe Dekoratoren und Weiterleitung, call/apply. Für einen genaueren Blick auf das this in Pfeilfunktionen, siehe Pfeilfunktionen erneut betrachtet.