Three little methods. One big idea: you control what this points to.
Every time you write a function in JavaScript, there's a hidden element — the this keyword. Usually, it works fine, but when you extract a method or pass it as an event listener, this can point somewhere unexpected, causing confusing bugs.
call, apply, and bind are the tools that help manage that hidden element. Let's explore with an example involving a bus company 🚌.
The Example Setup
Consider Purbasha Paribahan, a local bus company modeled as an object with a book method to record passenger reservations.
const purbasha = {
busName: 'Purbasha Paribahan',
busCode: 'PB',
bookings: [],
book: function(busNum, passengerName) {
console.log(
`${passengerName} has booked a seat on ${this.busName} with bus ${this.busCode}${busNum}.`
);
this.bookings.push({ bus: `${this.busCode}${busNum}`, passengerName });
}
};
Calling the function normally works perfectly:
purbasha.book(12, 'Sumon');
Output: Sumon has booked a seat on Purbasha Paribahan with bus PB12.
Inside book, this refers to purbasha — exactly as intended. But things get interesting when a rival enters the scene.
A competing company, Royal Express, needs similar booking functionality. Repeating the method isn't ideal, so we introduce our three heroes.
const royal = {
busName: 'Royal Express',
busCode: 'RE',
bookings: []
};
Method 1: call — Invoke Immediately with Arguments as a List
First, get the book function separately, then use .call() to invoke it with this set to the desired object, passing arguments individually.
const book = purbasha.book; // separate function reference
book.call(royal, 45, 'Tanvir');
Output: Tanvir has booked a seat on Royal Express with bus RE45.
What's happening?
callrunsbookimmediately, manually settingthistoroyal. It's like: "Use this function but treat it as if it belongs toroyal".
| Argument | Description |
|---|---|
| 1st | Object to assign this
|
| 2nd, 3rd, ... | Function arguments |
Method 2: apply — Same as call, Arguments in an Array
apply is similar but takes arguments as an array, useful when data is already in an array.
const passengerData = [23, 'Mahfuz'];
book.apply(purbasha, passengerData);
Output: Mahfuz has booked a seat on Purbasha Paribahan with bus PB23.
Tip: With ES6+, you can use the spread operator with
callinstead:
book.call(purbasha, ...passengerData);
| Method | Argument Passing |
|---|---|
| call | Individually listed |
| apply | In an array |
Method 3: bind — Create a New Function with this Set
bind doesn't run the function immediately. Instead, it returns a new function with this permanently set, which you can call later.
const bookRE = book.bind(royal); // new function
bookRE(56, 'Mustak'); // call the new function
Output: Mustak has booked a seat on Royal Express with bus RE56.
Using bind for Partial Application
bind is especially useful for partial application, where you preset some arguments.
// preset `this` and bus number 56
const bookRE56 = book.bind(royal, 56);
bookRE56('Robin');
bookRE56('Rafi');
Output:
Robin has booked a seat on Royal Express with bus RE56.
Rafi has booked a seat on Royal Express with bus RE56.
Note: Partial application is creating specialized functions by presetting some arguments.
Common Pitfall: Event Callbacks
Using object methods as event handlers often causes this to break. Here's an example:
purbasha.buses = 100;
purbasha.buyBus = function() {
this.buses++;
console.log(this.buses);
};
// ❌ Wrong — don't do this
document.querySelector('.buy')
.addEventListener('click', purbasha.buyBus);
Click, and you'll see NaN, not 101. That's because in event callbacks, this refers to the DOM element (the button), which lacks a buses property, leading to undefined++, resulting in NaN.
Rule of thumb: When passing methods as callbacks,
thiscan lose its original context.
To fix, bind the method:
// ✅ Correct — bind `purbasha` as 'this'
document.querySelector('.buy')
.addEventListener('click', purbasha.buyBus.bind(purbasha));
Now, this inside buyBus always refers to purbasha, no matter the event source.
Quick Reference
| Method | Executes? | Argument Passing | Use Case |
|---|---|---|---|
call |
Yes, immediately | Individual args | One-off with specific this
|
apply |
Yes, immediately | Arguments in array | When data is in an array |
bind |
No — returns a new func | Pre-set args optional | Callbacks, partials |
That's it! Understanding these methods as different ways to specify "this" makes them less mysterious and more natural to use.
Questions? Comments? Feel free to ask!
*Inspired by Jonas Schmedtmann's JavaScript course on Udemy.
Top comments (0)