
call() und apply() führen eine Funktion sofort mit einem gewählten this aus; call bekommt die Argumente einzeln, apply bekommt sie in einem Array. bind() führt nichts aus: es gibt eine neue Funktion zurück, deren this (und gegebenenfalls die ersten Argumente) dauerhaft festgelegt sind. Man nimmt es, wenn eine Methode als Callback übergeben wird und dabei ihr Objekt verliert.
Jede JavaScript-Funktion besitzt drei Methoden, call(), apply() und bind(), die derselben Sache dienen: festzulegen, was this beim Ausführen der Funktion bedeutet. Wozu sie taugen, versteht man erst über das Problem, das sie lösen: in JavaScript hängt this nicht davon ab, wo eine Funktion geschrieben steht, sondern davon, wie sie aufgerufen wird.
Das Problem: this wechselt mit dem Aufruf
Vier Regeln reichen, um this in fast allen Fällen vorherzusagen:
- Methodenaufruf
objet.fonction():thisistobjet. - Einfacher Aufruf
fonction():thisistundefinedim strikten Modus (Module, Klassen), das globale Objektwindowim nicht strikten Modus. - Aufruf mit
new:thisist das gerade konstruierte Objekt. - Aufruf über
call,applyoder eine mitbindgebundene Funktion:thisist das, was du festgelegt hast.
Pfeilfunktionen entziehen sich diesen Regeln: sie haben kein eigenes this und behalten das des umgebenden Codes.
Der Fall, der in der Praxis wehtut, ist der Wechsel von Regel 1 zu Regel 2:
const person = {
name: 'Sarah',
hello() {
return `Bonjour ${this.name}`;
},
};
person.hello(); // 'Bonjour Sarah' : Methodenaufruf, this = person
const detached = person.hello;
detached(); // TypeError im strikten Modus: this ist undefined
// ('Bonjour undefined' oder 'Bonjour ' im nicht strikten Modus)Die Funktion hat sich nicht geändert. Nur die Art des Aufrufs hat sich geändert, und this mit ihr. Genau das passiert, wenn eine Methode an setTimeout, an addEventListener oder an map übergeben wird.
call(): mit einem gewählten this aufrufen
fonction.call(thisArg, arg1, arg2, …) führt die Funktion sofort aus, mit this gleich thisArg und den Argumenten einzeln übergeben.
function presenter(age, ville) {
return `${this.name}, ${age} ans, ${ville}`;
}
const person = { name: 'Sarah' };
presenter.call(person, 30, 'Lille'); // 'Sarah, 30 ans, Lille'
presenter.call({ name: 'Léa' }, 25, 'Paris'); // 'Léa, 25 ans, Paris'
const detached = person.hello; // die Methode aus dem vorigen Beispiel
detached.call(person); // 'Bonjour Sarah' : this ist wiederhergestelltEin Klassiker: eine Array-Methode auf etwas anwenden, das gar kein Array ist.
Array.prototype.slice.call('abc'); // ['a', 'b', 'c']
// Heute schreibt man eher:
Array.from('abc'); // ['a', 'b', 'c']apply(): dasselbe, Argumente in einem Array
fonction.apply(thisArg, [arg1, arg2, …]) macht genau das, was call macht, bekommt die Argumente aber als Array. Praktisch, wenn sie ohnehin schon gebündelt ankommen.
presenter.apply(person, [30, 'Lille']); // 'Sarah, 30 ans, Lille'
const notes = [3, 9, 4];
Math.max.apply(null, notes); // 9 : Math.max erwartet einzelne Argumente, kein ArraySeit ES2015 wird apply durch den Spread-Operator nur noch selten gebraucht: Math.max(...notes) und presenter.call(person, ...args) leisten dasselbe und bleiben lesbar. apply begegnet dir vor allem in älterem Code.
bind(): this für später festlegen
Wo call und apply sofort ausführen, führt bind nichts aus. Es gibt eine neue Funktion zurück, deren this endgültig festgelegt ist. Das ist das Werkzeug für Callbacks: du bereitest die Funktion jetzt vor, jemand anderes ruft sie später auf.
const bound = person.hello.bind(person);
bound(); // 'Bonjour Sarah', unabhängig vom Aufrufkontext
bound.call({ name: 'X' }); // 'Bonjour Sarah' : bind gewinnt, call kann this nicht mehr ändern
bound.name; // 'bound hello'Der typische Fall: eine Methode als Callback
class Compteur {
constructor() {
this.n = 0;
}
incrementer() {
this.n += 1;
return this.n;
}
}
const compteur = new Compteur();
// Ohne bind: die Methode ist von ihrem Objekt gelöst
const inc = compteur.incrementer;
inc(); // TypeError: Cannot read properties of undefined (reading 'n')
// Mit bind: this ist auf compteur festgelegt
const incLie = compteur.incrementer.bind(compteur);
incLie(); // 1
incLie(); // 2Dasselbe Muster gilt für Timer und für Browser-Events:
setTimeout(compteur.incrementer.bind(compteur), 1000);
bouton.addEventListener('click', compteur.incrementer.bind(compteur));
// Um den Listener später entfernen zu können, behalte die gebundene Referenz:
const onClick = compteur.incrementer.bind(compteur);
bouton.addEventListener('click', onClick);
bouton.removeEventListener('click', onClick);Ohne bind würde addEventListener die Funktion incrementer mit this gleich dem geklickten Button aufrufen: this.n wäre undefined und der Zähler würde nichts zählen.
bind() für die partielle Anwendung
Die Argumente, die nach this an bind übergeben werden, sind ebenfalls festgelegt. Die zurückgegebene Funktion erwartet die restlichen.
const presenterSarah = presenter.bind(person, 30);
presenterSarah('Lille'); // 'Sarah, 30 ans, Lille' : nur das letzte Argument fehlt noch
const add = (a, b) => a + b;
const add5 = add.bind(null, 5); // this wird nicht gebraucht: null reicht
add5(3); // 8So spezialisiert man eine allgemeine Funktion, ohne eine Wrapper-Funktion zu schreiben.
Pfeilfunktionen: wann bind überflüssig wird
Eine Pfeilfunktion fängt das this der Stelle ein, an der sie geschrieben steht, und kümmert sich danach nicht mehr darum. Zwei Folgen.
Erstens können call, apply und bind ihr this nicht ändern:
const arrow = () => this;
arrow.call({ a: 1 }); // this des Moduls, nicht { a: 1 }Zweitens, und das ist der übliche Einsatz, behält eine in einer Methode oder einer Klasse deklarierte Pfeilfunktion das richtige this ohne bind:
class Compteur {
n = 0;
// Pfeil-Eigenschaft: this wird ein für alle Mal eingefangen
incrementer = () => {
this.n += 1;
return this.n;
};
}
const c = new Compteur();
const inc = c.incrementer;
inc(); // 1 : funktioniert ohne bind
setTimeout(() => c.incrementer(), 1000); // oder eine Pfeilfunktion beim AufrufDie Kehrseite: eine Pfeil-Eigenschaft wird für jede Instanz neu angelegt, während eine klassische Methode über den Prototyp geteilt wird. Bei ein paar Objekten spielt das keine Rolle; bei Tausenden von Instanzen spart die einmal im Konstruktor gebundene Methode (this.incrementer = this.incrementer.bind(this)) Speicher.
this als zweites Argument
Mehrere Array-Methoden nehmen den Wert von this, der im Callback gelten soll, direkt entgegen, was ein bind erspart:
const config = { k: 10 };
[1, 2, 3].map(function (x) { return this.k * x; }, config); // [10, 20, 30]forEach, filter, some und every haben dasselbe zweite Argument. Mit einer Pfeilfunktion wird es überflüssig.
Vergleichstabelle
call | apply | bind | |
|---|---|---|---|
| Führt die Funktion aus | sofort | sofort | nein: gibt eine Funktion zurück |
| Argumente | einzeln | in einem Array | einzeln, für spätere Aufrufe festgelegt |
| Typischer Einsatz | eine Methode ausleihen | ein Array ausbreiten (ersetzt durch ...) | Callbacks, Events, partielle Anwendung |
| Wirkung auf eine Pfeilfunktion | keine auf this | keine auf this | keine auf this, die Argumente werden festgelegt |
Häufige Fehler und wie du sie liest
- „Cannot read properties of undefined (reading ’n‘)“ in einer Klassenmethode: die Methode wurde von ihrer Instanz gelöst (Regel 2). Binde sie mit
bindoder deklariere sie als Pfeil-Eigenschaft. - Ein Event-Listener, der den Zustand nicht ändert:
thisist das DOM-Element, nicht dein Objekt. Dasselbe Mittel. - Ein
bind, das nichts zu bewirken scheint: die Funktion ist eine Pfeilfunktion. Entferne dasbind, es bringt nichts. removeEventListener, das nichts entfernt: jedesbinderzeugt eine neue Funktion; beim Hinzufügen und beim Entfernen muss dieselbe gebundene Referenz übergeben werden.
Weiter geht es mit den Klassen in JavaScript, die auf Methoden und Prototyp zurückkommen; das Kapitel Funktionen aus dem Tutorial legt die Grundlagen, falls das Vokabular dieses Artikels noch neu ist. In PHP stellt sich die Frage anders: $this wechselt nie das Objekt.
Häufige Fehler
bouton.addEventListener('click', compteur.incrementer) ruft die Funktion mit this gleich dem Button auf, nicht gleich compteur. Schreib compteur.incrementer.bind(compteur) oder eine Pfeilfunktion.this: call, apply und bind ändern es nicht. Nur die über bind übergebenen Argumente zählen.bind erzeugt bei jedem Aufruf eine neue Funktion. In einer Schleife oder bei wiederholtem Rendern binde einmal im Konstruktor (oder deklariere die Methode als Pfeil-Eigenschaft).f.call(obj, [1, 2]) übergibt ein einziges Argument, das Array. Zum Ausbreiten eines Arrays nimmst du apply oder f.call(obj, ...tableau).

