this in JavaScript ist berüchtigt — kein anderes Sprach-Detail führt zu so vielen Bug-Tickets. Die gute Nachricht: hinter dem Verhalten stehen vier klare Binding-Regeln, die je nach Aufruf-Form greifen. Default-Binding (freier Funktions-Aufruf), Implicit-Binding (Methoden-Aufruf), Explicit-Binding (call/apply/bind) und New-Binding (Konstruktor-Aufruf). Wer die Reihenfolge ihrer Anwendung kennt, kann this bei jeder Code-Stelle eindeutig bestimmen. Dazu kommt noch das lexical this der Arrow Functions, das gar keine eigenen Bindings hat — und der Unterschied zwischen strict und sloppy Mode für Default-Binding. Dieser Artikel erklärt alle Regeln mit Code und nennt die häufigsten Fallen.
Was ist this?
this ist ein implicit-übergebener Parameter jeder klassischen Funktion. Sein Wert hängt davon ab, wie die Funktion aufgerufen wird — nicht davon, wie oder wo sie definiert wurde.
function zeig() {
console.log('this:', this);
}
// Freier Aufruf
zeig(); // this = undefined (strict) / globalThis (sloppy)
// Als Methode
const obj = { name: 'A', zeig };
obj.zeig(); // this = obj
// Explicit
zeig.call({ name: 'B' }); // this = { name: 'B' }
// Als Konstruktor
new zeig(); // this = neues leeres Objectthis: undefined
this: { name: 'A', zeig: [Function: zeig] }
this: { name: 'B' }
this: zeig {}Dieselbe Funktion, vier verschiedene this-Werte — je nach Aufruf-Form.
Regel 1: Default-Binding (freier Aufruf)
Ein Funktions-Aufruf ohne Owner-Object und ohne Konstruktor liefert das Default-Binding. In Strict Mode: undefined. In Sloppy Mode: das globale Object (globalThis, window, global).
'use strict';
function f() {
console.log('strict:', this);
}
f(); // undefined — Default in Strict Modestrict: undefinedIn Sloppy Mode wäre this hier globalThis (in Browsern: window). ESM-Module sind automatisch im Strict Mode — daher ist Sloppy-Default heute selten Thema.
Regel 2: Implicit-Binding (Methoden-Aufruf)
Wenn eine Funktion über obj.methode() aufgerufen wird, ist this das Object links vom Punkt.
const user = {
name: 'Anna',
gruss() {
console.log('Hallo,', this.name);
},
};
user.gruss(); // this = user
const verschachtelt = {
innen: user,
};
verschachtelt.innen.gruss(); // this = user (letzter Punkt zählt)Hallo, Anna
Hallo, AnnaWichtig: nur das letzte Object vor dem Methoden-Aufruf zählt. Selbst bei tief verschachtelten Property-Zugriffen.
Implicit-Binding verlieren
Wer eine Methode aus dem Object entkoppelt — also nur die Funktion in einer Variable speichert oder als Callback weitergibt — verliert das Implicit-Binding.
const user = {
name: 'Anna',
gruss() {
console.log('Hallo,', this?.name);
},
};
user.gruss(); // Hallo, Anna
const ref = user.gruss;
ref(); // Hallo, undefined — Implicit weg, Default-Binding
setTimeout(user.gruss, 10); // Hallo, undefined — Callback verliert thisHallo, Anna
Hallo, undefined
Hallo, undefinedKlassische Falle: man übergibt eine Methode als Callback, und der Aufrufer ruft sie ohne den Owner-Kontext auf. Lösung: user.gruss.bind(user) oder ein Arrow-Wrapper () => user.gruss().
Regel 3: Explicit-Binding (call, apply, bind)
Mit call, apply und bind kann man this explizit setzen. Details im eigenen Artikel — hier nur die Übersicht.
function gruss(salut) {
console.log(`${salut}, ${this.name}!`);
}
const a = { name: 'Anna' };
const b = { name: 'Bob' };
gruss.call(a, 'Hi'); // call: this + Argumente einzeln
gruss.apply(b, ['Hey']); // apply: this + Argumente als Array
const grussFuerA = gruss.bind(a, 'Hallo');
grussFuerA(); // bind: liefert neue Funktion mit gebundenem thisHi, Anna!
Hey, Bob!
Hallo, Anna!Explicit-Binding ist stärker als Implicit-Binding — wenn fn.call(x) aufgerufen wird, gewinnt x, egal woher die Funktion kommt.
Regel 4: New-Binding (Konstruktor)
Mit new fn() passiert vier Dinge: ein neues leeres Object wird erzeugt, das Object wird mit dem Prototyp der Funktion verknüpft, die Funktion wird mit this = neues Object aufgerufen, und (sofern die Funktion keinen Object zurückgibt) wird this automatisch returnt.
function Auto(marke) {
this.marke = marke;
this.geschwindigkeit = 0;
}
Auto.prototype.beschleunigen = function() {
this.geschwindigkeit += 10;
};
const a = new Auto('VW');
a.beschleunigen();
a.beschleunigen();
console.log(a); // Auto { marke: 'VW', geschwindigkeit: 20 }Auto { marke: 'VW', geschwindigkeit: 20 }Moderne Codebasen nutzen für Konstruktoren typischerweise class, das intern dieselbe Mechanik implementiert.
Reihenfolge der Bindings
Wenn mehrere Regeln greifen könnten, ist die Priorität:
- New-Binding (
new fn()) — höchste Priorität - Explicit-Binding (
fn.call(obj),fn.apply(obj),fn.bind(obj)()) - Implicit-Binding (
obj.fn()) - Default-Binding (
fn()) — niedrigste Priorität
function f() { console.log(this?.name); }
const a = { name: 'A', f };
const b = { name: 'B' };
// 1: Implicit
a.f(); // 'A'
// 2: Explicit > Implicit
a.f.call(b); // 'B'
// 3: bind > Implicit, Explicit
const fb = f.bind(b);
const c = { name: 'C', m: fb };
c.m(); // 'B' — bind gewinnt
fb.call({ name: 'X' }); // 'B' — call kann bind nicht ändern
// 4: new > bind
function Constr() { this.name = 'Neues Object'; console.log(this.name); }
const NeuConst = Constr.bind(b);
new NeuConst(); // 'Neues Object' — new schlägt bindA
B
B
B
Neues ObjectArrow Functions: lexical this
Arrow Functions haben kein eigenes this. Sie übernehmen das this ihres umgebenden Scopes, lexikalisch bestimmt. Die vier Binding-Regeln gelten für sie nicht.
const obj = {
name: 'Anna',
klassisch() {
setTimeout(function() {
console.log('classic:', this?.name); // undefined
}, 0);
},
arrow() {
setTimeout(() => {
console.log('arrow: ', this?.name); // 'Anna'
}, 0);
},
};
obj.klassisch();
obj.arrow();classic: undefined
arrow: AnnaDas macht Arrows zur idealen Wahl für Inner-Callbacks in Methoden. call/apply/bind haben auf Arrows keinen Effekt — this bleibt lexikalisch.
Strict vs. Sloppy Mode
Der einzige Unterschied: das Default-Binding — also this bei freiem Funktions-Aufruf.
- Sloppy:
this = globalThis(windowim Browser,globalin Node). - Strict:
this = undefined.
// Sloppy
function sloppy() {
return this;
}
function strict() {
'use strict';
return this;
}
console.log(typeof sloppy()); // 'object' (globalThis)
console.log(typeof strict()); // 'undefined'object
undefinedESM-Module und class-Bodies sind automatisch Strict — daher ist Sloppy-Default-Binding in modernem Code selten Thema. Aber: in <script>-Tags ohne type="module" läuft Code per Default in Sloppy Mode.
Häufige Fallen — kompakte Übersicht
| Situation | Symptom | Lösung |
|---|---|---|
| Methode als Callback übergeben | this ist undefined | bind(obj) oder Arrow-Wrapper |
setTimeout(obj.method, ...) | this ist undefined | setTimeout(() => obj.method(), ...) |
| Methode aus Object destrukturieren | this ist undefined | nicht destrukturieren, oder bind |
| Arrow als Object-Methode | this ist nicht das Object | Methoden-Shorthand statt Arrow |
| Arrow als Event-Handler im DOM | this ist nicht das Element | klassische Funktion oder e.currentTarget |
| this in verschachtelter Funktion | innere Funktion hat eigenes this | innere als Arrow definieren |
new weggelassen | this = globalThis in Sloppy | class (wirft TypeError) oder Strict Mode |
Häufige Stolperfallen
Methode entkoppelt — this wird undefined
const ref = obj.method; ref(); verliert das Implicit-Binding. Häufig bei Callbacks: setTimeout(obj.method, ...), arr.forEach(obj.method), button.addEventListener('click', obj.method). Lösung: bind oder Arrow-Wrapper.
Arrow als Object-Methode — this zeigt nicht auf Object
const obj = { name: 'A', f: () => this.name } — die Arrow erbt this vom umgebenden Modul-Scope (in ESM: undefined, im Browser-Script: window). Lösung: Methoden-Shorthand { f() { ... } }.
setTimeout-Callback hat eigenes this — undefined in Strict
Ohne Bind oder Arrow ist this im setTimeout-Callback in Strict-Mode undefined. In Sloppy ist es globalThis. Empfehlung: immer Arrow-Function als Callback nutzen, dann erbt sie das umgebende this.
Verschachtelte klassische Funktionen verlieren this
Innerhalb einer Methode definiert man eine innere klassische Funktion — und in deren Body ist this nicht mehr das Owner-Object. Vor Arrow-Functions war const self = this der Standard-Hack. Heute: innere Funktion als Arrow definieren.
Class-Methode entkoppelt: TypeError statt undefined
Bei Class-Methoden ist der Body automatisch Strict. Wer eine entkoppelte Methode aufruft (const m = c.method; m();), bekommt einen TypeError beim ersten this.x-Zugriff — anders als bei Object-Methoden. Lösung: Arrow-Class-Field method = () => ... für auto-bind.
new bei Funktion ohne Schreibweisen-Konvention — bug-anfällig
Wer eine normale Funktion versehentlich mit new aufruft, bekommt ein leeres Object und potenziell unsinnige Properties. Schutz: class nutzen — Constructors ohne new werfen TypeError, vermeidbare Fehler werden laut. Bei traditionellen Constructors: if (!(this instanceof Foo))-Guard am Anfang.
bind erzeugt eine neue Funktion, Original bleibt
const bound = fn.bind(obj) liefert eine NEUE Funktion mit fixiertem this. fn selbst bleibt unverändert. Subtile Falle: man bindet zweimal — die zweite bind hat keinen Effekt mehr, weil bind-Funktionen bereits ein hardes this haben und nicht überschrieben werden.
Arrows ignorieren call/apply/bind
arrow.call(obj) ändert NICHT this der Arrow. Das macht Arrows angenehm-vorhersagbar — aber wer auf call-Mechanik angewiesen ist (z.B. Generic-Mixins), darf keine Arrow nehmen.
Weiterführende Ressourcen
Externe Quellen
- this – MDN
- Function.prototype.bind – MDN
- Strict mode – MDN
- The this Keyword – ECMAScript Spec
- You Don't Know JS: this & Object Prototypes