Understanding the this Keyword in JavaScript
What Is this?
In JavaScript, this is a special keyword that refers to the object that is currently calling the function. It is not fixed at the time you write the function — it is determined at the time the function is called.
Think of it like a nametag that gets filled in at the moment someone picks up the phone:
Who called this function right now?
That caller → that's `this`.
This single idea explains almost every this behaviour you'll encounter.
this in the Global Context
When you use this outside of any function or object — at the top level of your script — it refers to the global object.
console.log(this); // In a browser → Window object
In a browser, the global object is window. So this === window at the top level.
var name = "JavaScript";
console.log(this.name); // "JavaScript"
// same as window.name
Note: In Node.js, the global object is
global, notwindow. And in ES modules (type="module"),thisat the top level isundefined.
this Inside Objects
When a function is called as a method of an object, this refers to that object — the one before the dot.
const user = {
name: "Arjun",
greet() {
console.log("Hello, I am " + this.name);
}
};
user.greet(); // "Hello, I am Arjun"
Here, user.greet() is called on user. So inside greet, this is user. The dot tells you who the caller is.
Let's prove that this is truly dynamic:
const admin = {
name: "Priya"
};
admin.greet = user.greet; // same function, different caller
admin.greet(); // "Hello, I am Priya"
Same function. Different caller. Different this. This is the core behaviour.
this Inside Regular Functions
When a function is called standalone (not as a method), the caller is the global object — or undefined in strict mode.
function sayHello() {
console.log(this);
}
sayHello(); // Window (browser) or undefined (strict mode)
"use strict";
function sayHello() {
console.log(this); // undefined
}
sayHello();
In strict mode, JavaScript does not default this to the global object. This is intentional — it prevents accidental global variable pollution.
The Classic Trap: Method Detached from Object
const user = {
name: "Arjun",
greet() {
console.log("Hello, " + this.name);
}
};
const fn = user.greet; // detached — no longer called on user
fn(); // "Hello, undefined"
fn() has no caller object. So this defaults to global (or undefined in strict mode), and this.name is undefined. The function is the same — but the calling context changed.
How Calling Context Changes this
JavaScript gives you three explicit ways to control what this is: call, apply, and bind.
call() — Call with a specific this
function greet() {
console.log("Hello, " + this.name);
}
const user = { name: "Arjun" };
greet.call(user); // "Hello, Arjun"
call() invokes the function immediately, with user as this.
apply() — Same as call, but arguments as an array
function introduce(city, country) {
console.log(`\({this.name} from \){city}, ${country}`);
}
introduce.apply(user, ["Mumbai", "India"]);
// "Arjun from Mumbai, India"
bind() — Returns a new function with this permanently set
const boundGreet = greet.bind(user);
boundGreet(); // "Hello, Arjun"
// Even if you try to call it on another object, this stays as user
boundGreet.call({ name: "Someone else" }); // still "Hello, Arjun"
bind creates a new function where this is permanently locked — no matter how it is later called.
Arrow Functions: this Is Inherited, Not Assigned
Arrow functions are different. They do not have their own this. Instead, they inherit this from the surrounding scope where they were defined.
const user = {
name: "Arjun",
greet() {
const inner = () => {
console.log(this.name); // inherits `this` from greet()
};
inner();
}
};
user.greet(); // "Arjun" ✅
Compare this with a regular function:
const user = {
name: "Arjun",
greet() {
function inner() {
console.log(this.name); // own `this` → undefined in strict mode
}
inner();
}
};
user.greet(); // undefined ❌
Arrow functions are the idiomatic solution for callbacks inside methods, because they don't reset this.
const timer = {
name: "Countdown",
start() {
setTimeout(() => {
console.log(this.name + " started!"); // ✅ works
}, 1000);
}
};
timer.start(); // "Countdown started!"
Quick Summary Table
| Context | What this is |
|---|---|
| Global scope (browser) | window |
| Global scope (strict mode) | undefined |
Object method (obj.fn()) |
obj |
| Standalone function call | window / undefined |
call(obj) / apply(obj) |
obj |
bind(obj) |
obj (permanently) |
| Arrow function | Inherited from outer scope |
The One Rule to Remember
thisis always the object to the left of the dot when the function is called. If there's no dot, it's the global object (orundefinedin strict mode). Arrow functions don't play this game — they borrowthisfrom where they were born.
Once you internalise this, debugging this-related bugs becomes straightforward: trace back to where the function was called, not where it was defined.
Putting It All Together
const bank = {
owner: "Arjun",
balance: 5000,
// Regular method — `this` is the caller
showBalance() {
console.log(`\({this.owner}'s balance: ₹\){this.balance}`);
},
// Arrow inside method — inherits `this`
delayedShow() {
setTimeout(() => {
console.log(`Delayed: \({this.owner} has ₹\){this.balance}`);
}, 500);
}
};
bank.showBalance(); // "Arjun's balance: ₹5000"
bank.delayedShow(); // "Delayed: Arjun has ₹5000"
// Detach and call standalone
const fn = bank.showBalance;
fn(); // "undefined's balance: ₹undefined"
// Fix with bind
const fixed = bank.showBalance.bind(bank);
fixed(); // "Arjun's balance: ₹5000"
