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

Hola readers, this blog would be making your JS foundation stronger and understanding the fine details of JS, we would be discussing call(), apply(), bind() and this with some examples.
What's the point in this ?
In JS, this simply refers to the object that is currently executing the function. this depends on the environment where JS is present. In a browser this is the windows object.
this in node JS environment is different depending on the scope where it is used.
thisis a reference to the object that calls the function.
const person = {
name: "Saurav",
greet() {
console.log(this.name);
}
};
person.greet(); //Saurav
this inside Objects
In the above code block the out put is Saurav because greet is called as a method and normal function get this when they are called.
Now you might wonder why do we even need to call this.name, can't we simply console name as it is within the object itself?
const person = {
name: "Saurav",
greet() {
console.log(name);
}
};
person.greet(); //undefined
The problem with the above code block is that objects do not create a scope.
Scopes only comes from:
functions
blocks (
let/const)modules
In such cases it is this that tells JS to use the object that called this function.
so when we run person.greet() internally JS does this = person and when we do this.name, it is similar to saying person.name and it prints the name value inside the object.
Real Reason behind using this
this makes functions generic and reusable, by making this = whoever called the function. If objects created scope, this would break many patterns like:
method borrowing
prototypes
classes
call/apply/bind
functional reuse
So JavaScript instead used dynamic binding with this.
this inside normal functions
In my previous blog, I have discussed how this worked with arrow functions, in case you are interested then you should visit this
In a regular function, the value of this depends on how the function is called, not where it is written. this is decided at call time.
const person = {
name: "Saurav",
greet() {
console.log(this.name);
}
};
person.greet(); //Saurav
In the above code block greet is used as a method, and called using dot (.) notation, the object before dot (.) becomes the this for that function. So in above case
this = person
Now suppose you do something like :
const person = {
name: "Saurav",
greet() {
console.log(this.name);
}
};
const greetMe = person.greet;
greetMe() //undefined
we know that for a regular function this is decided at call time. So we are storing the reference of the greet() which is inside of person object inside the greetMe variable. And later on we do greetMe() . Now here during call time, greetMe is called as a normal function and is not associated with any object, so the value of this inside the greet() becomes as "", and when we try to access the name property then we get undefined as the answer.
call() me whenever you need me.
You went through this earlier on, you understood that how this was set for functions during call time. But consider a scenario, you want to set the value of this by yourself, in such a case, you gotta call call() .
functionName.call(thisArg, arg1, arg2)
Whatever object your want to associate with your function, you keep it inside the thisArg, and arg1, arg2, .... argN are the arguments passed to that function.
function greet() {
console.log("Hello " + this.name);
}
const user = { name: "Saurav" };
greet.call(user);
In the above code block, you are calling the function greet and using call to the set the value of this for it. Inside call you pass user object which means that inside the greet function now this = user, so when you do this.name, it becomes user.name.
Polyfill for call() (BONUS)
Function.prototype.myCall = function (context, ...args) {
// Step 1: Handle null/undefined
context = context || globalThis;
// Step 2: Create unique key to avoid override
const fnSymbol = Symbol();
// Step 3: Attach function to object
context[fnSymbol] = this;
// Step 4: Call function
const result = context[fnSymbol](...args);
// Step 5: Clean up
delete context[fnSymbol];
return result;
};
apply when-ever you want
call and apply almost do the same things, the only notable difference is that apply will pass arguments in the form of an array.
functionName.apply(thisArg, [arg1, arg2, arg3]);
In this you are passing thisArg which would be the this part and instead of passing arg1, arg2,...argN individually, we send it in an array.
function greet(city, country) {
console.log(this.name + " from " + city + ", " + country);
}
const person = { name: "Saurav" };
greet.apply(person, ["BBSR", "India"]);
//Output
//Saurav from BBSR, India
Inside the apply, what would be happening is that it is setting person.greet = greet and then calling person.greet("BBSR", "India");
Personally I feel apply could come handy when we already have arguments in an array.
function sum(a, b, c) {
return a + b + c;
}
const arr = [1, 2, 3];
sum.apply(null, arr); // 6
In the above code block, you can see that sum is a function that accepts three parameters, and we have an array of numbers, so instead of individually passing the numbers from the array as arguments, we simply use apply.
Modern alternative to apply
apply is old school, you will see modern codebases using it less and often do the above code block using the spread operator.
function sum(a, b, c) {
return a + b + c;
}
const arr = [1, 2, 3];
sum(...arr); // 6
Polyfill for apply()
Function.prototype.myApply = function (context, args) {
// Step 1: Handle null/undefined
context = context || globalThis;
// Step 2: Create unique key
const fnSymbol = Symbol();
// Step 3: Attach function
context[fnSymbol] = this;
// Step 4: Call with array
const result = args
? context[fnSymbol](...args)
: context[fnSymbol]();
// Step 5: Clean up
delete context[fnSymbol];
return result;
};
Bound to use bind() ?
bind() does NOT call the function immediately. It returns a new function with this permanently set.
const newFn = originalFn.bind(thisArg, arg1, arg2);
In the above syntax for bind() thisArg is this, arg1, arg2 are optional preset arguments. The thing which makes bind() different from it's counterparts call() and apply() is that call() does not run immediately, it returns a function which you call use it later on.
const person = {
name: "Saurav",
greet() {
console.log(this.name);
}
};
setTimeout(person.greet, 1000); //undefined
setTimeout(person.greet.bind(person), 1000); //Saurav
function greet(city) {
console.log(this.name + " from " + city);
}
const person = { name: "Saurav" };
const bound = greet.myBind(person, "BBSR");
bound(); // Saurav from BBSR
Real bind also works with new :
function Person(name) {
this.name = name;
}
const BoundPerson = Person.bind(null);
const p = new BoundPerson("Saurav");
console.log(p.name); // Saurav
Difference Table
| Feature | call() |
apply() |
bind() |
|---|---|---|---|
| Executes immediately? | ✅ Yes | ✅ Yes | ❌ No |
| Returns a function? | ❌ No | ❌ No | ✅ Yes |
| How arguments are passed? | Individually | As an array | Individually (preset) |
| Purpose | Call function with custom this |
Same as call but with array args | Create new function with fixed this |
| Use case | Direct invocation | When args are in array | Reuse later / callbacks |
| Syntax | fn.call(obj, a, b) |
fn.apply(obj, [a, b]) |
fn.bind(obj, a, b) |
| Partial application? | ❌ No | ❌ No | ✅ Yes |
| Used in async (setTimeout etc)? | ❌ No | ❌ No | ✅ Yes |
| Changes original function? | ❌ No | ❌ No | ❌ No |
call → call now
apply → call now (array)
bind → call later


