[Medium] 📄 this Binding
1. What is this in JavaScript?
Was ist
thisin JavaScript?
this ist ein Schlüsselwort in JavaScript, das auf das Kontextobjekt verweist, in dem eine Funktion ausgeführt wird. Der Wert von this hängt davon ab, wie die Funktion aufgerufen wird, und nicht davon, wo sie definiert wurde.
Binding-Regeln für this
Es gibt vier Regeln für das Binding von this in JavaScript (nach Priorität von hoch nach niedrig):
- new-Binding: Aufruf einer Konstruktorfunktion mit dem Schlüsselwort
new - Explizites Binding: Explizite Angabe von
thismitcall,apply,bind - Implizites Binding: Aufruf als Objektmethode
- Standard-Binding: Standardverhalten in allen anderen Fällen
2. Please explain the difference of this in different contexts
Bitte erklären Sie den Unterschied von
thisin verschiedenen Kontexten
1. this im globalen Kontext
console.log(this); // Browser: window, Node.js: global
function globalFunction() {
console.log(this); // Nicht-Strict-Modus: window/global, Strict-Modus: undefined
}
globalFunction();
'use strict';
function strictFunction() {
console.log(this); // undefined
}
strictFunction();
2. this in regulären Funktionen (Function)
Das this einer regulären Funktion hängt von der Aufrufart ab:
function regularFunction() {
console.log(this);
}
// Direkter Aufruf: this zeigt auf das globale Objekt (Nicht-Strict-Modus) oder undefined (Strict-Modus)
regularFunction(); // window (Nicht-Strict-Modus) oder undefined (Strict-Modus)
// Aufruf als Objektmethode: this zeigt auf das Objekt
const obj = {
method: regularFunction,
};
obj.method(); // obj
// Mit call/apply/bind: this zeigt auf das angegebene Objekt
const customObj = { name: 'Custom' };
regularFunction.call(customObj); // customObj
3. this in Arrow Functions
Arrow Functions haben kein eigenes this. Sie erben das this des umgebenden Gültigkeitsbereichs (lexikalischer Gültigkeitsbereich).
const obj = {
name: 'Object',
// Reguläre Funktion
regularMethod: function () {
console.log('regularMethod this:', this); // obj
// Innere reguläre Funktion: this ändert sich
function innerRegular() {
console.log('innerRegular this:', this); // window/undefined
}
innerRegular();
// Innere Arrow Function: this wird vom äußeren Bereich geerbt
const innerArrow = () => {
console.log('innerArrow this:', this); // obj
};
innerArrow();
},
// Arrow Function
arrowMethod: () => {
console.log('arrowMethod this:', this); // window (erbt vom globalen Bereich)
},
};
obj.regularMethod();
obj.arrowMethod();
4. this in Objektmethoden
const person = {
name: 'John',
age: 30,
// Reguläre Funktion: this zeigt auf person
greet: function () {
console.log(`Hello, I'm ${this.name}`); // "Hello, I'm John"
},
// ES6-Kurzschreibweise: this zeigt auf person
introduce() {
console.log(`I'm ${this.name}, ${this.age} years old`);
},
// Arrow Function: this erbt vom äußeren Bereich (hier global)
arrowGreet: () => {
console.log(`Hello, I'm ${this.name}`); // "Hello, I'm undefined"
},
};
person.greet(); // "Hello, I'm John"
person.introduce(); // "I'm John, 30 years old"
person.arrowGreet(); // "Hello, I'm undefined"
5. this in Konstruktorfunktionen
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function () {
console.log(`Hello, I'm ${this.name}`);
};
}
const john = new Person('John', 30);
john.greet(); // "Hello, I'm John"
console.log(john.name); // "John"
6. this in Klassen
class Person {
constructor(name) {
this.name = name;
}
// Reguläre Methode: this zeigt auf die Instanz
greet() {
console.log(`Hello, I'm ${this.name}`);
}
// Arrow-Function-Eigenschaft: this ist an die Instanz gebunden
arrowGreet = () => {
console.log(`Hi, I'm ${this.name}`);
};
}
const john = new Person('John');
john.greet(); // "Hello, I'm John"
// Zuweisung der Methode an eine Variable verliert das this-Binding
const greet = john.greet;
greet(); // Fehler: Cannot read property 'name' of undefined
// Arrow-Function-Eigenschaft verliert das this-Binding nicht
const arrowGreet = john.arrowGreet;
arrowGreet(); // "Hi, I'm John"
3. Quiz: What will be printed?
Quiz: Was wird ausgegeben?
Frage 1: Objektmethoden und Arrow Functions
const obj = {
name: 'Object',
regularFunc: function () {
console.log('A:', this.name);
},
arrowFunc: () => {
console.log('B:', this.name);
},
};
obj.regularFunc();
obj.arrowFunc();
Klicken Sie hier, um die Antwort zu sehen
// A: Object
// B: undefined
Erklärung:
regularFuncist eine reguläre Funktion, die überobj.regularFunc()aufgerufen wird.thiszeigt aufobj, daher wird"A: Object"ausgegebenarrowFuncist eine Arrow Function ohne eigenesthis. Sie erbt dasthisdes äußeren (globalen) Bereichs. Da es global keinname-Attribut gibt, wird"B: undefined"ausgegeben
Frage 2: Funktion als Argument übergeben
const person = {
name: 'John',
greet: function () {
console.log(`Hello, ${this.name}`);
},
};
person.greet(); // 1
const greet = person.greet;
greet(); // 2
setTimeout(person.greet, 1000); // 3
Klicken Sie hier, um die Antwort zu sehen
// 1: "Hello, John"
// 2: "Hello, undefined" oder Fehler (Strict-Modus)
// 3: "Hello, undefined" oder Fehler (Strict-Modus)
Erklärung:
person.greet()- Aufruf über das Objekt,thiszeigt aufpersongreet()- Nach Zuweisung der Methode an eine Variable gehtthisverloren und zeigt auf global oderundefinedsetTimeout(person.greet, 1000)- Die Methode wird als Callback übergeben,thisgeht verloren
Frage 3: Verschachtelte Funktionen
const obj = {
name: 'Outer',
method: function () {
console.log('A:', this.name);
function inner() {
console.log('B:', this.name);
}
inner();
const arrow = () => {
console.log('C:', this.name);
};
arrow();
},
};
obj.method();
Klicken Sie hier, um die Antwort zu sehen
// A: Outer
// B: undefined
// C: Outer
Erklärung:
A-methodwird überobjaufgerufen,thiszeigt aufobjB-innerist eine reguläre Funktion, die direkt aufgerufen wird.thiszeigt auf global oderundefinedC-arrowist eine Arrow Function, die dasthisder äußerenmethoderbt und aufobjzeigt
Frage 4: setTimeout und Arrow Functions
const obj = {
name: 'Object',
method1: function () {
setTimeout(function () {
console.log('A:', this.name);
}, 100);
},
method2: function () {
setTimeout(() => {
console.log('B:', this.name);
}, 100);
},
};
obj.method1();
obj.method2();
Klicken Sie hier, um die Antwort zu sehen
// A: undefined
// B: Object
Erklärung:
A- Der Callback vonsetTimeoutist eine reguläre Funktion. Bei der Ausführung zeigtthisauf das globale ObjektB- Der Callback vonsetTimeoutist eine Arrow Function, die dasthisder äußerenmethod2erbt und aufobjzeigt
Frage 5: Komplexes this-Binding
const obj1 = {
name: 'obj1',
getThis: function () {
return this;
},
};
const obj2 = {
name: 'obj2',
};
console.log('A:', obj1.getThis().name);
const getThis = obj1.getThis;
console.log('B:', getThis() === window); // Annahme: Browser-Umgebung
obj2.getThis = obj1.getThis;
console.log('C:', obj2.getThis().name);
const boundGetThis = obj1.getThis.bind(obj2);
console.log('D:', boundGetThis().name);
Klicken Sie hier, um die Antwort zu sehen
// A: obj1
// B: true
// C: obj2
// D: obj2
Erklärung:
A- Aufruf überobj1,thiszeigt aufobj1B- Direkter Aufruf,thiszeigt auf global (window)C- Aufruf überobj2,thiszeigt aufobj2D- Mitbindwirdthisanobj2gebunden
Frage 6: Konstruktorfunktion und Prototyp
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
console.log(`Hello, I'm ${this.name}`);
};
Person.prototype.delayedGreet = function () {
setTimeout(function () {
console.log('A:', this.name);
}, 100);
};
Person.prototype.arrowDelayedGreet = function () {
setTimeout(() => {
console.log('B:', this.name);
}, 100);
};
const john = new Person('John');
john.delayedGreet();
john.arrowDelayedGreet();
Klicken Sie hier, um die Antwort zu sehen
// A: undefined
// B: John
Erklärung:
A- Regulärer Funktions-Callback vonsetTimeout,thiszeigt auf globalB- Arrow-Function-Callback vonsetTimeout, erbt dasthisder äußerenarrowDelayedGreetund zeigt aufjohn
Frage 7: Globale Variable vs Objekteigenschaft
var name = 'jjjj';
var obj = {
a: function () {
name = 'john';
console.log(this.name);
},
};
obj.a();
Klicken Sie hier, um die Antwort zu sehen
// undefined
Erklärung:
Der Schlüssel zu dieser Frage liegt im Verständnis des Unterschieds zwischen globalen Variablen und Objekteigenschaften:
-
Worauf zeigt
thisbeiobj.a():- Aufruf über
obj.a(),thiszeigt aufobj
- Aufruf über
-
name = 'john'ändert die globale Variable:name = 'john'; // Ohne var/let/const wird die globale name geändert
// Entspricht
window.name = 'john'; // Browser-Umgebung -
this.namegreift auf die Objekteigenschaft zu:console.log(this.name); // Entspricht console.log(obj.name) -
Das
obj-Objekt hat keinename-Eigenschaft:obj.name; // undefined (im obj-Objekt ist name nicht definiert)
Vollständiger Ausführungsablauf:
// Anfangszustand
window.name = 'jjjj'; // Globale Variable
obj = {
a: function () { /* ... */ },
// Beachte: obj hat keine name-Eigenschaft!
};
// Ausführung von obj.a()
obj.a();
↓
// 1. name = 'john' → Ändert globales window.name
window.name = 'john'; // ✅ Globale Variable wurde geändert
// 2. this.name → Zugriff auf obj.name
this.name; // Entspricht obj.name
obj.name; // undefined (obj hat keine name-Eigenschaft)
Häufige Missverständnisse:
Viele denken, es wird 'john' ausgegeben, weil:
- ❌ Sie annehmen, dass
name = 'john'demobjeine Eigenschaft hinzufügt - ❌ Sie annehmen, dass
this.nameauf die globale Variable zugreift
Richtiges Verständnis:
- ✅
name = 'john'ändert nur die globale Variable und hat keinen Einfluss aufobj - ✅
this.namegreift aufobj.namezu, nicht auf die globalename
Um 'john' auszugeben, sollte man so schreiben:
var obj = {
a: function () {
this.name = 'john'; // ✅ Fügt obj eine name-Eigenschaft hinzu
console.log(this.name); // 'john'
},
};
obj.a(); // Gibt 'john' aus
console.log(obj.name); // 'john'
Frage 8: Globale-Variablen-Falle (Erweiterung)
var name = 'global';
var obj = {
name: 'object',
a: function () {
name = 'modified'; // Beachte: Kein var/let/const
console.log('1:', name); // Zugriff auf globale Variable
console.log('2:', this.name); // Zugriff auf Objekteigenschaft
},
};
obj.a();
console.log('3:', name); // Globale Variable
console.log('4:', obj.name); // Objekteigenschaft
Klicken Sie hier, um die Antwort zu sehen
// 1: modified
// 2: object
// 3: modified
// 4: object
Erklärung:
// Anfangszustand
window.name = 'global'; // Globale Variable
obj.name = 'object'; // Objekteigenschaft
// Ausführung von obj.a()
name = 'modified'; // Ändert globales window.name
console.log('1:', name); // Zugriff auf global: 'modified'
console.log('2:', this.name); // Zugriff auf obj.name: 'object'
// Nach der Ausführung
console.log('3:', name); // Global: 'modified'
console.log('4:', obj.name); // Objekt: 'object' (nicht geändert)
Schlüsselkonzepte:
name(ohnethis.) → Zugriff auf globale Variablethis.name(mitthis.) → Zugriff auf Objekteigenschaft- Die beiden sind völlig verschiedene Variablen!
4. How to preserve this in callbacks?
Wie bewahrt man
thisin Callback-Funktionen?
Methode 1: Arrow Functions verwenden
const obj = {
name: 'Object',
method: function () {
// ✅ Arrow Function erbt this vom äußeren Bereich
setTimeout(() => {
console.log(this.name); // "Object"
}, 1000);
},
};
obj.method();
Methode 2: bind() verwenden
const obj = {
name: 'Object',
method: function () {
// ✅ bind bindet this
setTimeout(
function () {
console.log(this.name); // "Object"
}.bind(this),
1000
);
},
};
obj.method();
Methode 3: this in einer Variablen speichern (alte Methode)
const obj = {
name: 'Object',
method: function () {
// ✅ this in einer Variablen speichern
const self = this;
setTimeout(function () {
console.log(self.name); // "Object"
}, 1000);
},
};
obj.method();
Methode 4: call() oder apply() verwenden
function greet() {
console.log(`Hello, I'm ${this.name}`);
}
const person1 = { name: 'John' };
const person2 = { name: 'Jane' };
greet.call(person1); // "Hello, I'm John"
greet.apply(person2); // "Hello, I'm Jane"
5. Common this pitfalls
Häufige Fallstricke bei
this
Fallstrick 1: Objektmethode einer Variablen zuweisen
const obj = {
name: 'Object',
greet: function () {
console.log(this.name);
},
};
obj.greet(); // ✅ "Object"
const greet = obj.greet;
greet(); // ❌ undefined (this geht verloren)
// Lösung: bind verwenden
const boundGreet = obj.greet.bind(obj);
boundGreet(); // ✅ "Object"
Fallstrick 2: this in Event-Listenern
const button = document.querySelector('button');
const obj = {
name: 'Object',
// ❌ Arrow Function: this zeigt nicht auf button
handleClick1: () => {
console.log(this); // window
},
// ✅ Reguläre Funktion: this zeigt auf das Element, das das Event ausgelöst hat
handleClick2: function () {
console.log(this); // button-Element
},
// ✅ Wenn auf das this des Objekts zugegriffen werden muss, Arrow Function als Wrapper verwenden
handleClick3: function () {
button.addEventListener('click', () => {
console.log(this.name); // "Object"
});
},
};
Fallstrick 3: Callbacks in Array-Methoden
const obj = {
name: 'Object',
items: [1, 2, 3],
// ❌ Regulärer Funktions-Callback verliert this
processItems1: function () {
this.items.forEach(function (item) {
console.log(this.name, item); // undefined 1, undefined 2, undefined 3
});
},
// ✅ Arrow-Function-Callback behält this bei
processItems2: function () {
this.items.forEach((item) => {
console.log(this.name, item); // "Object" 1, "Object" 2, "Object" 3
});
},
// ✅ thisArg-Parameter von forEach verwenden
processItems3: function () {
this.items.forEach(function (item) {
console.log(this.name, item); // "Object" 1, "Object" 2, "Object" 3
}, this); // Zweiter Parameter gibt this an
},
};
6. this binding rules summary
Zusammenfassung der
this-Binding-Regeln
Priorität (von hoch nach niedrig)
// 1. new-Binding (höchste Priorität)
function Person(name) {
this.name = name;
}
const john = new Person('John');
console.log(john.name); // "John"
// 2. Explizites Binding (call/apply/bind)
function greet() {
console.log(this.name);
}
const obj = { name: 'Object' };
greet.call(obj); // "Object"
// 3. Implizites Binding (Objektmethode)
const obj2 = {
name: 'Object2',
greet: greet,
};
obj2.greet(); // "Object2"
// 4. Standard-Binding (niedrigste Priorität)
greet(); // undefined (Strict-Modus) oder window.name
Vergleichstabelle: Function vs Arrow Function
| Eigenschaft | Function | Arrow Function |
|---|---|---|
Hat eigenes this | ✅ Ja | ❌ Nein |
this hängt ab von | Aufrufart | Definitionsort (lexikalischer Gültigkeitsbereich) |
this änderbar mit call/apply/bind | ✅ Ja | ❌ Nein |
| Kann als Konstruktor verwendet werden | ✅ Ja | ❌ Nein |
Hat arguments-Objekt | ✅ Ja | ❌ Nein |
| Geeignet für | Objektmethoden, Konstruktoren | Callbacks, wenn äußeres this geerbt werden muss |
Merkregel
„Arrow erbt, Funktion ruft auf"
- Arrow Function:
thiserbt vom äußeren Gültigkeitsbereich, wird bei der Definition festgelegt- Reguläre Funktion:
thishängt von der Aufrufart ab, wird zur Laufzeit festgelegt
7. Best practices
Best Practices
✅ Empfohlene Vorgehensweisen
// 1. Für Objektmethoden reguläre Funktionen oder ES6-Methodenkurzschreibweise verwenden
const obj = {
name: 'Object',
// ✅ Gut: Reguläre Funktion
greet: function () {
console.log(this.name);
},
// ✅ Gut: ES6-Kurzschreibweise
introduce() {
console.log(this.name);
},
};
// 2. Für Callback-Funktionen Arrow Functions verwenden
class Component {
constructor() {
this.name = 'Component';
}
mount() {
// ✅ Gut: Arrow Function behält this bei
setTimeout(() => {
console.log(this.name);
}, 1000);
}
}
// 3. Bei Bedarf an dynamischem this reguläre Funktionen verwenden
const button = {
label: 'Click me',
// ✅ Gut: Zugriff auf this des DOM-Elements erforderlich
handleClick: function () {
console.log(this); // button-DOM-Element
},
};
❌ Nicht empfohlene Vorgehensweisen
// 1. Arrow Functions nicht für Objektmethoden verwenden
const obj = {
name: 'Object',
// ❌ Schlecht: this zeigt nicht auf obj
greet: () => {
console.log(this.name); // undefined
},
};
// 2. Arrow Functions nicht als Konstruktoren verwenden
// ❌ Schlecht: Arrow Functions können nicht als Konstruktoren verwendet werden
const Person = (name) => {
this.name = name; // Fehler!
};
// 3. Arrow Functions nicht verwenden, wenn Zugriff auf arguments benötigt wird
// ❌ Schlecht: Arrow Functions haben kein arguments
const sum = () => {
console.log(arguments); // ReferenceError
};