Mastering call, apply, and bind — Practical Guide to Control "this" (Part 2)
In Part 1, we understood how this works and why it often behaves unexpectedly.
You can check part 1 : https://medium.com/@abhijitpatil669/understanding-this-in-javascript-a-practical-guide-to-stop-the-confusion-fd092b274785
Now comes the real question:
What do we do when this goes wrong?
How do we control it?
That’s exactly where call, apply, and bind comes in.
By the end of this article, you should be able to:
- Recognize when
thiswill break - Fix it confidently
- Understand what happens internally
- Solve interview questions easily
First, Build the Right Mental Model
Before jumping into the methods, get this clear:
JavaScript decides this at execution time, based on how a function is called.
So when this feels wrong, it’s usually not a bug—it’s just JavaScript doing what it’s supposed to.
call, apply, and bind give you a way to take control when needed.
The Problem Pattern (Very Important)
A lot of real-world bugs come from this pattern:
const obj = {
name: "JS",
print() {
console.log(this.name);
}
};
const fn = obj.print;
fn();What’s actually happening?
Call stack will call “fn()” Not “obj.print()”
So:
thisis lost- Output: undefined
Key Insight
Whenever a function is called without an object, this is usually not what you expect.
1. call() — Execute Immediately with Correct this
Think of it as:
“Run this function right now with this object”
Example:
function greet() {
console.log(this.name);
}
const user = { name: "Abhijit" };
greet.call(user);What’s happening internally?
Instead of “greet()”
It behaves like:
greet.call(user);
// similar to:
user.greet();Call Stack View: greet() (with this = user)
When to use:
- Reusing functions across objects
- Immediate execution
- Simple scenarios
2. apply() — Same as call, but with Array
Think of it as:
“Run this function now with this object and arguments as an array”
Example:
function introduce(age, city) {
console.log(this.name, age, city);
}
const user = { name: "Abhijit" };
introduce.apply(user, [25, "Pune"]);When to use:
- When arguments come as an array (API data, dynamic input)
3. bind() — Fix this for Later
Think of it as:
“Create a new function where this is permanently fixed”
Get Abhijit Patil’s stories in your inbox
Join Medium for free to get updates from this writer.
Example:
function greet() {
console.log(this.name);
}
const user = { name: "Abhijit" };
const fn = greet.bind(user);
fn();What’s happening internally?
const fn = greet.bind(user);Creates a new function like:
function () {
return greet.call(user);
}Call Stack View:
fn()
-> greet() with this = userWhen to use:
- Callbacks
- Event handlers
- Async code
- Anywhere
thismight get lost
Real-World Case 1: setTimeout Bug
const user = {
name: "Abhijit",
greet() {
setTimeout(function () {
console.log(this.name);
}, 1000);
}
};Execution Flow:
- greet() runs with this = user
- setTimeout registers callback
- After 1 second: “callback()” NOT “user.callback()”
Problem:
- this becomes window or undefined
Fix:
setTimeout(function () {
console.log(this.name);
}.bind(this), 1000);Learning:
- When function runs later (async),
thiscan be lost - Use bind or arrow functions
Real-World Case 2: Event Handlers
const button = {
text: "Click",
handleClick() {
console.log(this.text);
}
};
document.addEventListener("click", button.handleClick);What happens?
handleClick()
"this" is lostFix:
document.addEventListener(
"click",
button.handleClick.bind(button)
);Learning:
- Always bind when passing object methods as event handlers
Pattern Recognition (Most Important Skill)
These patterns break this:
setTimeout(obj.method, 1000);
const fn = obj.method;
return obj.method;Fix using:
.bind(obj)
OR
() => obj.method()Execution-Level Understanding
Without bind:
callback()
-> this = windowWith bind:
callback()
-> behaves like:
callback.call(user)
-> this = userWhen to Use What
Practice (Think Before Answer)
function show() {
console.log(this.name);
}
const user = { name: "JS" };
setTimeout(show, 1000);
// Fix itconst obj = {
name: "JS",
print() {
console.log(this.name);
}
};
const fn = obj.print;
fn();
// fix itTricky Interview Questions
var name = "Global";
const obj = {
name: "Object",
getName() {
return this.name;
}
};
const fn = obj.getName;
console.log(fn());
// Answer:
// Browser → "Global"
// Node → undefinedconst obj = {
name: "JS",
print() {
console.log(this.name);
}
};
setTimeout(obj.print, 1000);
// OUTPUT : Undefined
// FIX:
setTimeout(obj.print.bind(obj), 1000);const obj = {
name: "JS",
print() {
return function () {
console.log(this.name);
};
}
};
obj.print()();
// Output: undefinedfunction show() {
console.log(this.name);
}
const user = { name: "A" };
const fn = show.bind(user);
fn.call({ name: "B" });
// Output: "A"
// Reason: bind cannot be overriddenfunction add(a, b) {
console.log(this.name, a + b);
}
const obj = { name: "Test" };
add.apply(obj, [1, 2]);
// Output: "Test 3"const obj = {
name: "JS",
print() {
console.log(this.name);
}
};
(obj.print = obj.print)();
// Output: undefined
// Reason: assignment breaks referenceconst obj = {
name: "JS",
print() {
console.log(this.name);
}
};
const fn = obj.print.bind(obj);
setTimeout(fn, 1000);
//Output: "JS"
// Reason: bind permanently fixes thisExecution Mindset
Whenever you see a function, ask:
How is it being called?
Case 1:
fn()
-> this lost
Case 2:
obj.fn()
-> this = obj
Case 3:
fn.bind(obj)()
-> this = objFinal Takeaway
- this breaks when a function loses its object
- call and apply fix it immediately
- bind fixes it permanently
- Most bugs happen in async or detached functions
Final Mental Model
Whenever you see a function, ask:
“How will this function be called?”
If it's: callback()
this is gone
If it’s:obj.method()
this is safeOnce you start spotting these patterns, you won’t just understand this — you’ll be able to predict it.






