this, call(), apply(), bind()
this keyword
First we discuss about 'this' keyword. Simply put, this is a value that changes depending on context — where it appears determines what it refers to. It's especially useful in object-oriented programming.
Let's look at a problem first, then see how this solves it.
The Problem:-
const guest = {
name: 'manas',
greet(){
console.log(`good morning ${name}`);
}
}
guest.greet();
ReferenceError: name is not defined
What's going wrong here? Inside the greet method, JavaScript looks for a variable called name in the local scope — and finds nothing. The method has no idea that a name property exists right there on the same object. This is a very common frustration: you want to access an object's own data from within one of its methods.
The Solution:-
const guest = {
name: 'manas',
greet(){
console.log(`good morning ${this.name}`);
}
}
guest.greet();
good morning manas
When greet is called on guest, this points to the object that owns the method — in this case, guest itself. So this.name gives us 'manas' exactly as expected.
Think of this as the method saying: "who called me? That's who I'm referring to."
the value of this
the value of this depends on in which context it appears means in global scope, inside object method, inside a function or in a class.
Function context:-
inside in function this is like a hidden parameter, this depends how function is called. This actually refers to a object where function is called.
if we called a function in global context the value of this is refereed to Window (in browser) or global object (node js) .
function greet() {
console.log(this);
}
greet();
// In browser: Window { ... }
// In Node.js: Object [global] { ... }
but here comes a catch with strict mode, in strict mode this does not fallback to global object, then the value of this is undefined.
'use strict';
function greet() {
console.log(this);
}
greet();
undefined
This is actually safer it prevents you from accidentally reading or writing properties on the global object.
this in Object Methods:-
When a function is called as a method on an object, this is simply that object. Clean and straightforward.
const user = {
name: 'Manas',
greet() {
console.log(`Hi, I am ${this.name}`);
}
}
user.greet(); // Hi, I am Manas
greet is called on user, so this is user. Simple.
The trouble starts when you pull the method out of the object:
const user = {
name: 'Manas',
greet() {
console.log(`Hi, I am ${this.name}`);
}
}
const fn = user.greet;
fn(); // ❌ Hi, I am undefined
The function is the same — but now it's called as a plain function, not on any object. So this loses its reference to user and falls back to the global object.
thisis about how a function is called, not where it was defined.
this in Arrow Functions — Why They're Different
Before we talk about arrow functions, let's quickly recap what we know.
In regular functions, this is dynamic — it's decided at call time, depending on who calls the function. We saw that calling a function on an object sets this to that object, and calling it plainly gives you the global object (or undefined in strict mode).
Arrow functions throw that rule out the window. Arrow Functions Don't Have Their Own this. This is the single most important thing to understand. When you write an arrow function, it does not create its own this. Instead, it looks outward and borrows this from the surrounding scope where it was defined. This is called lexical this.
Let's see what that means in practice.
The Problem Arrow Functions Solve
Imagine you have a timer object with a method that counts down, using setTimeout internally:
const timer = {
seconds: 3,
start() {
setTimeout(function() {
console.log(`Time left: ${this.seconds}`);
}, 1000);
}
}
timer.start();
Time left: undefined
Wait — why undefined? You'd expect this.seconds to be 3.
Here's what's happening: the callback passed to setTimeout is a regular function. By the time it runs, it's no longer being called as a method on timer. It's called by the browser's timer engine as a plain function — so this becomes the global object, which has no seconds property.
This used to be one of the most common bugs in JavaScript.
const timer = {
seconds: 3,
start() {
setTimeout(() => {
console.log(`Time left: ${this.seconds}`);
}, 1000);
}
}
timer.start();
Time left: 3
It works. But why?
When the arrow function is defined, it looks at the surrounding scope — which is the start method. At the time start runs, this is timer (because start was called as a method on timer). The arrow function captures that this and keeps it forever, no matter how or when it's eventually called.
const classroom = {
teacher: 'Manas',
students: ['Alice', 'Bob', 'Charlie'],
announce() {
this.students.forEach(student => {
console.log(`\({this.teacher} is teaching \){student}`);
});
}
}
classroom.announce();
Manas is teaching Alice
Manas is teaching Bob
Manas is teaching Charlie
The arrow function inside forEach doesn't create its own this. It looks outward to announce, where this is classroom. So this.teacher correctly gives us 'Manas' on every iteration.
If you had used a regular function inside forEach, this.teacher would be undefined.
Call()
if want to manually pass a specific this value to a function then we use call() method. we use it when we don't want to follow rules I want this to be exactly this object.
here a example code
function introduce(city, country) {
console.log(`Hi, I am \({this.name}, from \){city}, ${country}`);
}
const person1 = { name: 'Manas' };
const person2 = { name: 'Alice' };
introduce has no object — it's just a standalone function. Normally this.name would be undefined. But watch what happens when we use call()
call invokes the function immediately and lets you pass the desired this as the first argument. Any additional arguments follow one by one
introduce.call(person1, 'Kolkata', 'India');
introduce.call(person2, 'London', 'UK');
Hi, I am Manas, from Kolkata, India
Hi, I am Alice, from London, UK
Same function, completely different this each time. You're essentially borrowing the function and running it in a different context.
apply()
apply is identical to call in behavior — it also invokes the function immediately. The only difference is that instead of passing arguments one by one, you pass them as a single array.
introduce.apply(person1, ['Kolkata', 'India']);
introduce.apply(person2, ['London', 'UK']);
Hi, I am Manas, from Kolkata, India
Hi, I am Alice, from London, UK
Same result. The difference is just the shape of how you pass arguments.
bind()
bind() is different and interesting. It says "Don't Call Yet, Just Lock In this"
Here's where bind stands apart. Unlike call and apply, bind does not invoke the function immediately. Instead, it returns a brand new function with this permanently locked in — ready to be called whenever you need.
const introduceManas = introduce.bind(person1, 'Kolkata', 'India');
// nothing has run yet...
introduceManas(); // called later
Hi, I am Manas, from Kolkata, India
summery
introduce.call(person1, 'Kolkata', 'India'); // runs now, args separate
introduce.apply(person1, ['Kolkata', 'India']); // runs now, args as array
const fn = introduce.bind(person1, 'Kolkata', 'India'); fn(); // runs later