Understanding this, call(), apply(), and bind() in JavaScript

If you've ever stared at the word this in JavaScript and had no idea what it was referring to — you're not alone. It's one of the most confusing parts of the language for beginners. But once you crack it, everything starts to make sense.
Let's break it down simply, with examples you can run right in your browser console.
What Is this?
Here's the simplest way to think about it:
thisrefers to whoever is calling the function.
It's not about where the function was written. It's about who triggered it at the moment it runs.
Think of a function like a worker. this is the worker asking: "Who hired me right now? I'll work for them."
[ Function ]
↑
| "Who called me?"
|
[ The Caller ] ← this = the caller
this Inside Normal Functions
When you call a function on its own (not attached to an object), this refers to the global object — which is window in a browser.
function greet() {
console.log(this);
}
greet(); // logs the window object (in a browser)
In strict mode, this inside a standalone function is undefined instead.
"use strict";
function greet() {
console.log(this); // undefined
}
greet();
Takeaway: Standalone functions have unpredictable
this. The magic happens when functions are attached to objects.
this Inside Objects
When a function lives inside an object and is called through that object, this refers to that object.
const user = {
name: "Sarah",
greet: function () {
console.log("Hello, my name is " + this.name);
},
};
user.greet(); // "Hello, my name is Sarah"
Here, user is calling greet(), so this is user. Simple!
Now what if we try to borrow that function?
const greetFn = user.greet;
greetFn(); // "Hello, my name is undefined" ⚠️
The function lost its connection to user. It's no longer being called through an object, so this no longer knows who it belongs to.
This is exactly the problem that call(), apply(), and bind() solve.
call() — Borrow a Function and Run It Now
call() lets you run a function and manually tell it what this should be.
function introduce(city, country) {
console.log(`I'm \({this.name} from \){city}, ${country}.`);
}
const person = { name: "Carlos" };
introduce.call(person, "Madrid", "Spain");
// "I'm Carlos from Madrid, Spain."
Syntax:
functionName.call(thisValue, arg1, arg2, ...)
You pass the object you want as this first, then the arguments one by one.
Borrowing a method between objects
const dog = {
name: "Buddy",
describe: function () {
console.log(this.name + " is a good dog.");
},
};
const cat = { name: "Whiskers" };
dog.describe.call(cat); // "Whiskers is a good dog."
cat borrowed dog's method using call(). The function ran as if it belonged to cat.
apply() — Like call(), but Arguments Go in an Array
apply() works exactly like call(), with one difference: you pass arguments as an array instead of one by one.
function introduce(city, country) {
console.log(`I'm \({this.name} from \){city}, ${country}.`);
}
const person = { name: "Aisha" };
const details = ["Lagos", "Nigeria"];
introduce.apply(person, details);
// "I'm Aisha from Lagos, Nigeria."
Syntax:
functionName.apply(thisValue, [arg1, arg2, ...])
When is apply() useful?
When your arguments are already in an array — like data coming from an API or user input — apply() saves you from spreading them manually.
const numbers = [3, 7, 1, 9, 4];
console.log(Math.max.apply(null, numbers)); // 9
(We pass null here because Math.max doesn't use this.)
bind() — Create a New Function with this Locked In
Unlike call() and apply(), bind() does not run the function immediately. Instead, it returns a brand new function where this is permanently set to whatever you choose.
function greet() {
console.log("Hello, I'm " + this.name);
}
const player = { name: "Jordan" };
const greetJordan = greet.bind(player);
// Nothing ran yet. We stored the bound function.
greetJordan(); // "Hello, I'm Jordan"
greetJordan(); // "Hello, I'm Jordan" (still works later)
Syntax:
const newFn = functionName.bind(thisValue, arg1, arg2, ...)
Why is bind() useful?
It's especially handy when passing methods as callbacks, where the original this would otherwise be lost.
const timer = {
message: "Time's up!",
start: function () {
setTimeout(this.ring.bind(this), 1000);
},
ring: function () {
console.log(this.message);
},
};
timer.start(); // "Time's up!" (after 1 second)
Without bind(this), the setTimeout callback would lose the reference to timer.
call vs apply vs bind — Side by Side
| Feature | call() |
apply() |
bind() |
|---|---|---|---|
| Runs immediately? | Yes | Yes | No — returns a new function |
| How args are passed | One by one | As an array [ ] |
One by one (at bind time or call time) |
| Returns | The function's result | The function's result | A new bound function |
| Best used when | You want to run it now with specific args | Your args are already in an array | You want to save it and call it later |
Putting It All Together
const doctor = {
name: "Dr. Patel",
diagnose: function (symptom, severity) {
console.log(`\({this.name} says: \){symptom} is ${severity}.`);
},
};
const nurse = { name: "Nurse Kim" };
// call() — run now, pass args one by one
doctor.diagnose.call(nurse, "fever", "mild");
// "Nurse Kim says: fever is mild."
// apply() — run now, pass args as array
doctor.diagnose.apply(nurse, ["headache", "moderate"]);
// "Nurse Kim says: headache is moderate."
// bind() — save for later
const nurseCheck = doctor.diagnose.bind(nurse);
nurseCheck("fatigue", "severe");
// "Nurse Kim says: fatigue is severe."
Practice Assignment
Work through these steps to solidify the concepts:
1. Create an object with a method using this:
const car = {
brand: "Toyota",
describe: function () {
console.log("This car is a " + this.brand);
},
};
car.describe(); // works as expected
2. Borrow that method using call():
const bike = { brand: "Yamaha" };
car.describe.call(bike); // "This car is a Yamaha"
3. Use apply() with array arguments:
function showDetails(year, color) {
console.log(`\({this.brand} — \){year} — ${color}`);
}
const details = [2022, "red"];
showDetails.apply(car, details); // "Toyota — 2022 — red"
showDetails.apply(bike, details); // "Yamaha — 2022 — red"
4. Use bind() and store the function:
const describeBike = car.describe.bind(bike);
// Call it whenever you need
describeBike(); // "This car is a Yamaha"
describeBike(); // still works!
Quick Recap
thisalways refers to whoever is calling the function at runtimeInside objects,
thisis the object itselfStandalone functions can lose
this— that's where the three methods come incall()— runs immediately, arguments passed one by oneapply()— runs immediately, arguments passed as an arraybind()— doesn't run immediately, returns a new function withthislocked in
Master these three and you'll never feel confused by this again.
Happy coding! 🚀
If you enjoyed this article, check out my other blogs on this profile.
🔗 Connect with me:
LinkedIn | GitHub | X (Twitter)




