Losing context
Why this gets lost
this in JS depends on how a function is called, not on where it is declared.
If you pass a method as a reference, it is no longer called "through the object", and this becomes undefined (or window in non-strict mode):
const user = {
name: 'Tim',
sayHi() {
console.log(`Hello, I am ${this.name}`);
}
};
setTimeout(user.sayHi, 1000); // "Hello, I am undefined"Why this happens:
setTimeoutcalls the function ascallback(), not asuser.sayHi(), sothisloses its connection to theuserobject.
Method 1 - bind()
The Function.prototype.bind() method creates a new function with a permanently bound context.
setTimeout(user.sayHi.bind(user), 1000); // Hello, I am Timbind returns a new function in which this always points to user, regardless of how it is called.
Method 2 - arrow functions
Arrow functions do not have their own this,
they inherit it from the outer context (lexically).
const user = {
name: 'Tim',
sayHiLater() {
setTimeout(() => {
console.log(`Hello, I am ${this.name}`);
}, 1000);
}
};
user.sayHiLater(); // Hello, I am TimHere the arrow function "captured" this from sayHiLater.
Method 3 - save this into a variable
The classic "old" way, before arrow functions existed.
const user = {
name: 'Tim',
sayHiLater() {
const self = this;
setTimeout(function() {
console.log(`Hello, I am ${self.name}`);
}, 1000);
}
};
user.sayHiLater(); // Hello, I am TimHere self (or that) keeps a reference to the user context.
Method 4 - call the function via call or apply
You can manually set the context at call time.
function greet() {
console.log(`Hello, ${this.name}`);
}
const user = { name: 'Tim' };
greet.call(user); // Hello, Tim
greet.apply(user); // Hello, TimThis is a temporary binding: it only works at the moment of the call.
Method 5 - use a class (methods are called on an instance)
Inside classes, this is usually not lost if you call the method through an instance:
class User {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(`Hello, ${this.name}`);
}
}
const tim = new User('Tim');
tim.sayHi(); // Hello, TimBut if the method is passed "detached", the context is lost again:
const fn = tim.sayHi;
fn(); // undefinedTo avoid this, you can bind the method in the constructor below.
Method 6 - binding in the constructor (for classes and React)
class User {
constructor(name) {
this.name = name;
this.sayHi = this.sayHi.bind(this); // permanently fix the context
}
sayHi() {
console.log(`Hello, ${this.name}`);
}
}
const tim = new User('Tim');
setTimeout(tim.sayHi, 1000); // Hello, I am TimThis is especially useful in React classes (this.handleClick = this.handleClick.bind(this)).
Method 7 - class fields
A modern approach: methods declared as arrow functions inside a class automatically inherit this.
class User {
name = 'Tim';
sayHi = () => {
console.log(`Hello, ${this.name}`);
};
}
const tim = new User();
setTimeout(tim.sayHi, 1000); // Hello, TimThis is ES2022 syntax (supported in all modern browsers).
Method 8 - do not pass the method directly
Sometimes it is simpler to just call the method inside an anonymous wrapper,
so as not to lose this:
setTimeout(() => user.sayHi(), 1000); // Hello, I am TimHere user.sayHi() is called with the correct context.
Summary
| Method | What it does | Where it is often used |
|---|---|---|
.bind(this) | Creates a function with a fixed context | Callbacks, React, timers |
| Arrow function | Inherits this from the outer scope | setTimeout, async, promises |
self = this | Old way to save a reference | Legacy code, callbacks |
call / apply | Temporarily sets the context | One-off calls |
| Binding in a class | Via bind in the constructor or an arrow function | React classes, OOP |
| Anonymous wrapper | Calls the method within the context | Simple callbacks |
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.