Losing the this context
Losing the context means calling an object's method without going through the object, so that this inside it no longer points to that object. In JavaScript this is not baked into a function at declaration time: it is resolved on every call, based on how the function was invoked.
Theory
TL;DR
thisis set by the call site: what stands to the left of the dot matters, not where the function was declared.- Passing a method by reference (
setTimeout(user.sayHi)) breaks the link to the object. bind(obj)returns a new function with a permanently fixed context.- Arrow functions have no
thisof their own and take it lexically from the enclosing scope. call/applyset the context temporarily, only for the duration of one call.- In classes you fix the context with
bindin the constructor or with a class field holding an arrow function.
Quick example
const user = {
name: 'Oleh',
sayHi() {
console.log(`Hi, I am ${this.name}`);
}
};
user.sayHi(); // "Hi, I am Oleh"
setTimeout(user.sayHi, 1000); // "Hi, I am undefined"Why this gets lost
setTimeout receives only a reference to the function and invokes it as callback(), not as user.sayHi(). There is no object to the left of the dot any more, so the binding to user disappears:
- in strict mode (and ES modules are always strict)
thisisundefined, and readingthis.namethrows aTypeError; - in sloppy mode
thisis substituted with the global object (windowin the browser), andthis.namequietly returnsundefined.
The same happens with any detached method: event handlers, array.map(obj.method), storing a method in a variable.
Pinning the context: bind, call and apply
Function.prototype.bind() creates a new function with a hard-wired context, no matter how it is called later:
setTimeout(user.sayHi.bind(user), 1000); // "Hi, I am Oleh"call and apply, by contrast, do not create a new function: they invoke the existing one with the given context right now. That is a temporary binding, valid only for this call:
function greet() {
console.log(`Hi, ${this.name}`);
}
const user = { name: 'Oleh' };
greet.call(user); // "Hi, Oleh"
greet.apply(user); // "Hi, Oleh"The only difference between them is argument passing: call takes them as a list, apply as an array.
Arrow functions, the self variable and wrappers
An arrow function has no this of its own: it takes it from the scope where it was created. Inside a method it therefore closes over the correct context:
const user = {
name: 'Oleh',
sayHiLater() {
setTimeout(() => {
console.log(`Hi, I am ${this.name}`);
}, 1000);
}
};
user.sayHiLater(); // "Hi, I am Oleh"Before arrow functions existed, the same effect was achieved by saving the context into a self or that variable:
const user = {
name: 'Oleh',
sayHiLater() {
const self = this;
setTimeout(function() {
console.log(`Hi, I am ${self.name}`);
}, 1000);
}
};
user.sayHiLater(); // "Hi, I am Oleh"The simplest option, when you control the place where the callback is passed, is not to pass the method directly at all but to call it inside an anonymous wrapper:
setTimeout(() => user.sayHi(), 1000); // "Hi, I am Oleh"Here user.sayHi() is called through the object, so the context is correct.
Context inside classes
In a class this usually survives, as long as the method is called through an instance:
class User {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(`Hi, ${this.name}`);
}
}
const oleh = new User('Oleh');
oleh.sayHi(); // "Hi, Oleh"But detach the method from the instance and the context is gone again (class bodies always run in strict mode, so this throws):
const fn = oleh.sayHi;
fn(); // TypeError: Cannot read properties of undefinedThe classic fix is binding in the constructor:
class User {
constructor(name) {
this.name = name;
this.sayHi = this.sayHi.bind(this); // fix the context once and for all
}
sayHi() {
console.log(`Hi, ${this.name}`);
}
}
const oleh = new User('Oleh');
setTimeout(oleh.sayHi, 1000); // "Hi, Oleh"This is exactly how React class components were written for years: this.handleClick = this.handleClick.bind(this).
The more modern option is class fields (ES2022): a method declared as an arrow function automatically inherits the instance's this:
class User {
name = 'Oleh';
sayHi = () => {
console.log(`Hi, ${this.name}`);
};
}
const oleh = new User();
setTimeout(oleh.sayHi, 1000); // "Hi, Oleh"Class field syntax is supported by every modern browser. The price is that such a function is created per instance instead of living once on the prototype.
Summary table
| Approach | What it does | Where it is used |
|---|---|---|
.bind(this) | Creates a new function with a fixed context | Callbacks, React, timers |
| Arrow function | Inherits this from the enclosing scope | setTimeout, async, promises |
const self = this | The old way to keep a reference to the context | Legacy code, callbacks |
call / apply | Sets the context temporarily, for one call | One-off invocations |
| Binding in a class | bind in the constructor or an arrow class field | React classes, OOP |
| Anonymous wrapper | Calls the method through the object | Simple callbacks |
Common mistakes
- Assuming
thisdepends on where the function is declared. It depends only on how it is called; the same function can have a differentthison every call. - Calling
bindinside render or inside a handler.bindreturns a new function every time, so reference comparison breaks andremoveEventListenercannot detach the listener. Bind once, in the constructor or in a class field. - Trying to "reassign"
thison an arrow function.arrow.call(obj)andarrow.bind(obj)have no effect: an arrow has nothisof its own. - Using an arrow as an object literal method.
{ name: 'Oleh', sayHi: () => this.name }takesthisfrom the enclosing scope, not from the object. - Forgetting that the failure is silent. In sloppy mode nothing throws: you simply see
undefinedinstead of the value.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.