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

🤔 What is this?
Here's the simplest way to think about it:
this is whatever object is calling the function.
That's it. this is not a fixed value. It changes depending on who calls the function.
Think of it like asking "Who sent this message?" The answer changes depending on who actually sent it.
🔍 this Inside a Normal Function
When you call a function on its own (not inside any object), this refers to the global object — which is window in a browser.
function showThis() {
console.log(this);
}
showThis(); // window (in browser)
In strict mode, this inside a standalone function is undefined:
"use strict";
function showThis() {
console.log(this); // undefined
}
showThis();
Don't worry too much about window and strict mode for now. Just remember: a standalone function has no clear "owner", so this defaults to global or undefined.
🏠 this Inside an Object
This is where this becomes super useful. When a function lives inside an object (called a method), this refers to that object.
const person = {
name: "Arjun",
greet: function() {
console.log("Hello, I am " + this.name);
}
};
person.greet(); // Hello, I am Arjun
Why does this.name give "Arjun"? Because person is the one calling greet(). The object before the dot is always this.
// The object before the dot ↓ is `this`
person.greet();
// ^^^^^^ this = person
Four practical rules to determine this
Default binding: standalone function → global object (or
undefinedin strict mode).Implicit binding: obj.method() →
thisis the object before the dot.Explicit binding:
call/apply/bind→thisis what you pass.newbinding: when called withnew,thisis the newly created instance.
🪄 Now the Magic: Borrowing Functions
Here's a question. What if you have another object that doesn't have a greet method, but you want to use person's greet for it?
That's exactly what call(), apply(), and bind() are for — they let you borrow a function and tell it which object to use as this.
📞 call(), apply(), and bind() — borrow a function and set this
These three methods let you explicitly choose what this should be when a function runs.
call(thisArg, arg1, arg2, ...)— invoke the function immediately withthisset tothisArgand arguments listed individually.apply(thisArg, [argsArray])— invoke immediately withthisset tothisArgand arguments provided as an array.bind(thisArg, ...presetArgs)— return a new function permanently bound tothisArg(and optionally preset arguments); it does not call the function immediately.
Examples
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
const person = { name: 'Arjun' };
const stranger = { name: 'Sam' };
// call: args listed
greet.call(person, 'Hi', '!'); // Hi, Arjun!
// apply: args as array
greet.apply(stranger, ['Hello', '...']); // Hello, Sam...
// bind: returns a new function bound to `person`
const greetArjun = greet.bind(person, 'Hey');
greetArjun('?'); // Hey, Arjun?
When to use which
Use
callwhen you want to call a function right now and have arguments handy as separate values.Use
applywhen your arguments are already in an array (common when forwarding arguments).Use
bindwhen you need a stable function reference that keeps the intendedthisfor later use (callbacks, event listeners, timers).
Common scenario: method extracted and losing this
const user = {
name: 'Lina',
sayName() {
console.log(this.name);
}
};
const fn = user.sayName;
fn(); // undefined (or global name) — lost implicit binding
const boundFn = user.sayName.bind(user);
boundFn(); // Lina — `this` restored
Arrow functions and this
Arrow functions do not have their own
this. They inheritthislexically from the surrounding scope.This makes arrow functions useful when you want to preserve the outer
thiswithout usingbind.
Example: arrow function avoids bind inside a method
const obj = {
name: 'Maya',
delayedGreet() {
setTimeout(() => {
// arrow function uses `this` from delayedGreet's scope
console.log('Hello, ' + this.name);
}, 100);
}
};
obj.delayedGreet(); // Hello, Maya
📊 call vs apply vs bind — At a Glance
[call vs apply vs bind Comparison Table]
call() |
apply() |
bind() |
|
|---|---|---|---|
| Calls immediately? | ✅ Yes | ✅ Yes | ❌ No |
| Arguments passed as | Comma separated | Array [ ] |
Comma separated |
| Returns | Result of the call | Result of the call | A new function |
| Use when | Borrow once | Args in an array | Save & call later |
| Example | fn.call(obj, a, b) |
fn.apply(obj, [a,b]) |
fn.bind(obj) |
✍️ Practice Assignment
Try this step by step in your browser console (F12 → Console).
Task 1 — Create an object with this
const doctor = {
name: "Dr. Mehta",
introduce: function(speciality, hospital) {
console.log(`I am \({this.name}, a \){speciality} at ${hospital}.`);
}
};
// Direct call — this = doctor
doctor.introduce("Cardiologist", "Apollo");
// I am Dr. Mehta, a Cardiologist at Apollo.
Task 2 — Borrow using call()
const nurse = { name: "Anjali" };
// Borrow doctor's introduce for nurse
doctor.introduce.call(nurse, "Nurse", "Lilavati");
// I am Anjali, a Nurse at Lilavati.
Task 3 — Use apply() with an array
const args = ["Surgeon", "Kokilaben"];
doctor.introduce.apply(nurse, args);
// I am Anjali, a Surgeon at Kokilaben.
Task 4 — Use bind() to save for later
const patient = { name: "Ramesh" };
const patientIntro = doctor.introduce.bind(patient);
// Call later — this is permanently patient
patientIntro("Patient", "City Hospital");
// I am Ramesh, a Patient at City Hospital.
🎯 Quick Recap
this= the object that is currently calling the functionThe object before the dot is always
thiscall()— borrows a function and runs it now, args comma-separatedapply()— same ascall(), but args passed as an arraybind()— returns a new function withthislocked in, doesn't run immediatelyAll three let you manually control what
thismeans inside a function