The Magic of this, call(), apply(), and bind() in JavaScript

I build clean and simple web experiences and learn something new every day.
If you’ve been learning JavaScript for a while, you’ve probably seen the keyword this and thought:
Wait… what exactly is
thispointing to here?
And then later you discover call(), apply(), and bind() and things become even more confusing. But don’t worry. In this blog I’m going to explain everything step by step.
By the end of this post you’ll clearly understand:
what
thisreally meanshow functions decide what
thisrefers tohow
call(),apply(), andbind()let you controlthismanually
Let’s start from the very beginning.
What Does this Mean in JavaScript?
The easiest way to understand this is this simple idea this refers to the object that is calling the function. Not where the function was written. Not where it exists in the file. It depends on who is calling it.
Think about it like this imagine someone shouts:
Hey, introduce yourself!
Who should speak?
It depends on who was asked. JavaScript works in a similar way.
A Simple Rule That Helps a Lot
Most of the time you can use this rule - this is the object to the left of the dot when a function is called.
person.greet()
person.greet()
this = person
So inside greet(), the value of this will be person.
this Inside a Normal Function
Let’s look at a basic function and see how it works inside a normal function.
function show() {
console.log(this);
}
show();
Here the function is called without any object. So JavaScript doesn’t know who owns it. In browsers, this becomes the global object (window).
In strict mode it becomes undefined.
"use strict";
function show() {
console.log(this);
}
show(); // undefined
That’s why beginners sometimes get confused here. The function has no owner, so JavaScript uses the global context.
this Inside an Object
Now things start making more sense here.
const user = {
name: "Nausheen",
greet() {
console.log(`Hello, I'm ${this.name}`);
}
};
user.greet(); // Hello I'm Nausheen
Why?
because:
user.greet()
this = user
So this.name becomes "Nausheen"
When a Function Loses Its Context
Now watch something interesting here.
const user = {
name: "Nausheen",
greet() {
console.log(`Hello, I'm ${this.name}`);
}
};
const sayHello = user.greet;
sayHello(); // Hello, I'm undefined
Why?
Because now the function is called like this:
sayHello()
There is no object calling it anymore. So this lost its original context. And this exact problem is where call(), apply(), and bind() become useful.
Understanding the Function - Caller Relationship
You can imagine it like this:
Object ------ calls ------> Function
person.greet()
this = person
But if the function is called alone:
greet()
this = global / undefined
So sometimes we need a way to tell the function who its caller should be. That’s exactly what these methods do.
What call() Does
call() lets you run a function and manually set what this should be.
function introduce(city, hobby) {
console.log(`I'm \({this.name} from \){city} and I like ${hobby}`);
}
const person = {
name: "Aisha"
};
introduce.call(person, "Delhi", "reading");
I'm Aisha from Delhi and I like reading
Explanation:
introduce.call(person, "Delhi", "reading")
this = person
city = "Delhi"
hobby = "reading"
The function runs immediately. And the first argument always becomes this.
Method Borrowing Using call()
A very common use of call() is borrowing a method from another object.
const developer = {
name: "Rahul",
introduce() {
console.log(`Hi I'm ${this.name}`);
}
};
const designer = {
name: "Priya"
};
developer.introduce.call(designer);
Hi I'm Priya
Here we used developer's method for designer without copying the function.
What apply() Does
apply() works almost exactly like call(). The only difference is how arguments are passed.
call()
Arguments are passed one by one
function show(city, hobby) {}
show.call(user, "Mumbai", "coding");
apply()
Arguments are passed inside an array
function show(city, hobby) {}
show.apply(user, ["Mumbai", "coding"]);
function introduce(city, hobby) {
console.log(`\({this.name} lives in \){city} and likes ${hobby}`);
}
const user = {
name: "Kabir"
};
const details = ["Pune", "music"];
introduce.apply(user, details);
Kabir lives in Pune and likes music
So remember:
call - arguments separately
apply - arguments in an array
What bind() Does
bind() is a little different. Instead of running the function immediately, it returns a new function. And in that new function, this is permanently set.
function greet() {
console.log(`Hello ${this.name}`);
}
const user = {
name: "Sara"
};
const newGreet = greet.bind(user);
At this point nothing runs yet. newGreet is just a new function. Now when we call it:
newGreet(); // Hello Sara
And no matter where you call it later, this will always stay user.
Why bind() Is Useful
bind() is often used when passing functions around. For example in callbacks or timers.
const timer = {
message: "Time finished",
show() {
console.log(this.message);
},
start() {
setTimeout(this.show.bind(this), 2000);
}
};
timer.start();
Without bind, this.message might become undefined. bind() keeps the correct object context.
call vs apply vs bind
Here’s the easiest way to compare them.
| Feature | call() | apply() | bind() |
|---|---|---|---|
| Executes immediately | Yes | Yes | No |
| Returns | Function result | Function result | New function |
| Arguments | Passed one by one | Passed as array | Passed one by one |
| Main use | Method borrowing | When arguments are already in array | Store function for later |
Simple memory trick: call & apply run immediately, bind prepares a function for later
Small Practice Assignment
Try this small exercise to make the concept clear.
Step 1 Create an object
const developer = {
name: "Arjun",
language: "JavaScript",
introduce() {
console.log(`Hi I'm \({this.name} and I use \){this.language}`);
}
};
Step 2 Create another object
const designer = {
name: "Riya",
language: "Figma"
};
Step 3 Borrow the method using call()
developer.introduce.call(designer);
Step 4 Use apply()
function showSkills(skill1, skill2) {
console.log(`\({this.name} knows \){skill1} and ${skill2}`);
}
const skills = ["UI Design", "Prototyping"];
showSkills.apply(designer, skills);
Step 5 Use bind()
const devSkills = showSkills.bind(developer);
devSkills("React", "Node");
Try changing the objects and run the code yourself. That’s the best way to really understand how this behaves.
Final Thoughts
Once you understand one simple idea, everything becomes clear: this depends on who calls the function.
Then the rest becomes tools to control it.
call()=> run the function immediately with a chosenthisapply()=> same as call but arguments come as an arraybind()=> create a new function withthispermanently set
Whenever you feel confused, just ask yourself:
Who is calling this function right now?
If you answer that question, you’ll almost always know what this is. And once that clicks, these concepts stop feeling like magic and start feeling completely predictable.

