Skip to main content

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):

javascript
const user = { name: 'Tim', sayHi() { console.log(`Hello, I am ${this.name}`); } }; setTimeout(user.sayHi, 1000); // "Hello, I am undefined"

Why this happens:

  • setTimeout calls the function as callback(), not as user.sayHi(), so this loses its connection to the user object.

Method 1 - bind()

The Function.prototype.bind() method creates a new function with a permanently bound context.

javascript
setTimeout(user.sayHi.bind(user), 1000); // Hello, I am Tim

bind 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).

javascript
const user = { name: 'Tim', sayHiLater() { setTimeout(() => { console.log(`Hello, I am ${this.name}`); }, 1000); } }; user.sayHiLater(); // Hello, I am Tim

Here the arrow function "captured" this from sayHiLater.


Method 3 - save this into a variable

The classic "old" way, before arrow functions existed.

javascript
const user = { name: 'Tim', sayHiLater() { const self = this; setTimeout(function() { console.log(`Hello, I am ${self.name}`); }, 1000); } }; user.sayHiLater(); // Hello, I am Tim

Here 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.

javascript
function greet() { console.log(`Hello, ${this.name}`); } const user = { name: 'Tim' }; greet.call(user); // Hello, Tim greet.apply(user); // Hello, Tim

This 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:

javascript
class User { constructor(name) { this.name = name; } sayHi() { console.log(`Hello, ${this.name}`); } } const tim = new User('Tim'); tim.sayHi(); // Hello, Tim

But if the method is passed "detached", the context is lost again:

javascript
const fn = tim.sayHi; fn(); // undefined

To avoid this, you can bind the method in the constructor below.


Method 6 - binding in the constructor (for classes and React)

javascript
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 Tim

This 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.

javascript
class User { name = 'Tim'; sayHi = () => { console.log(`Hello, ${this.name}`); }; } const tim = new User(); setTimeout(tim.sayHi, 1000); // Hello, Tim

This 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:

javascript
setTimeout(() => user.sayHi(), 1000); // Hello, I am Tim

Here user.sayHi() is called with the correct context.


Summary

MethodWhat it doesWhere it is often used
.bind(this)Creates a function with a fixed contextCallbacks, React, timers
Arrow functionInherits this from the outer scopesetTimeout, async, promises
self = thisOld way to save a referenceLegacy code, callbacks
call / applyTemporarily sets the contextOne-off calls
Binding in a classVia bind in the constructor or an arrow functionReact classes, OOP
Anonymous wrapperCalls the method within the contextSimple callbacks

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.