Understanding the this Keyword in JavaScript
thisdoesn't mean "this function" or "this file". It means "whoever called me". That one shift in thinking makes everything click.
Why this Confuses People
this is one of those things that seems simple at first, and then behaves in ways that feel completely unexpected. You write a method on an object, call it from somewhere else, and suddenly this is undefined. Or worse, it's the window object. Nothing makes sense.
The confusion comes from a common assumption: that this refers to the place where you wrote the function. It doesn't.
this refers to the place where you called the function.
That distinction is everything. this is determined at the moment a function is called, not when it's defined. Once you accept that, this becomes completely predictable.
What this Represents
this is a keyword that refers to an object. Which object? The one that is currently "responsible" for running the code — the one that called the function.
Think of it like this: imagine you work at a company and your job is to write reports. The report always starts with "As requested by ___". The name that goes in the blank depends on who asked for the report, your manager, a client, the CEO. The task (writing the report) is the same. But this (who asked for it) changes based on the context.
Same function. Different caller. Different this.
this in the Global Context
When code runs at the top level, not inside any function or object, this refers to the global object.
In a browser, the global object is window:
console.log(this); // window (in a browser)
console.log(this === window); // true
Any variable you declare at the global level with var becomes a property of window:
var city = "Mumbai";
console.log(window.city); // "Mumbai"
console.log(this.city); // "Mumbai"
This is rarely useful in practice, but it's good to know what this defaults to when there's no other context. Think of it as: when nobody specific called the function, JavaScript falls back to the global object as the caller.
In strict mode, this at the global level inside a regular function becomes undefined instead of window. More on that shortly.
this Inside an Object Method
This is the most natural use of this, and the one that makes the most intuitive sense.
When you call a method on an object, this inside that method refers to the object the method was called on:
const user = {
name: "Priya",
greet: function() {
console.log("Hello, I'm " + this.name);
}
};
user.greet(); // "Hello, I'm Priya"
When you write user.greet(), you're calling greet on user. So inside greet, this is user. That's why this.name gives you "Priya".
Let's make this more concrete with a few properties:
const bankAccount = {
owner: "Arjun",
balance: 5000,
deposit: function(amount) {
this.balance += amount;
console.log(`\({this.owner} deposited ₹\){amount}. New balance: ₹${this.balance}`);
},
withdraw: function(amount) {
if (amount > this.balance) {
console.log("Insufficient funds.");
return;
}
this.balance -= amount;
console.log(`\({this.owner} withdrew ₹\){amount}. New balance: ₹${this.balance}`);
}
};
bankAccount.deposit(1000);
// "Arjun deposited ₹1000. New balance: ₹6000"
bankAccount.withdraw(2000);
// "Arjun withdrew ₹2000. New balance: ₹4000"
Every time you call a method with object.method(), the this inside that method is the object on the left side of the dot. That's the rule.
this Inside Regular Functions
Here's where things start to get surprising.
When you call a regular function, not as a method on an object, just a plain function call, this is not what you might expect:
function showThis() {
console.log(this);
}
showThis(); // window (in browser, non-strict mode)
No object called this function. So JavaScript falls back to the global object (window) as the default. It's not ideal, but that's how it works in non-strict mode.
In strict mode, JavaScript doesn't fall back to window. Instead, this is undefined:
"use strict";
function showThis() {
console.log(this); // undefined
}
showThis();
This is actually the safer behavior, it catches mistakes earlier. If you accidentally call a constructor function without new, for example, strict mode will throw an error instead of silently polluting window.
The Classic Trap: Losing this
This is where most this bugs happen. You have a method on an object, and you pull it out and call it separately, and this breaks:
const user = {
name: "Zara",
greet: function() {
console.log("Hello, I'm " + this.name);
}
};
user.greet(); // "Hello, I'm Zara" — works fine
const greetFn = user.greet; // pull the function out
greetFn(); // "Hello, I'm undefined" — broken!
What happened? When you call user.greet(), the object user is on the left of the dot, so this is user. But when you call greetFn(), there's no object on the left of the dot. It's a plain function call. So this falls back to window (or undefined in strict mode), and window.name is not "Zara".
This exact scenario causes bugs in:
// Event listeners
const button = {
label: "Submit",
handleClick: function() {
console.log("Clicked:", this.label);
}
};
// This breaks — the function is detached from the object
document.querySelector("button").addEventListener("click", button.handleClick);
// "Clicked: undefined" — because `this` is now the button DOM element, not our object
Understanding this trap is half the battle. Once you see why it happens, the fixes make complete sense.
How Calling Context Changes this
this is completely determined by how a function is called. There are four main calling patterns, and each one sets this differently.
Pattern 1: Method Call — this is the object
const obj = {
name: "Priya",
getName: function() { return this.name; }
};
obj.getName(); // this = obj → "Priya"
Object on the left of the dot → this is that object.
Pattern 2: Plain Function Call — this is global (or undefined)
function sayHello() {
console.log(this);
}
sayHello(); // this = window (or undefined in strict mode)
No object on the left → this defaults to global.
Pattern 3: call and apply — You Choose this
JavaScript lets you manually set what this should be when calling a function, using .call() or .apply():
function greet() {
console.log("Hello, I'm " + this.name);
}
const person1 = { name: "Priya" };
const person2 = { name: "Arjun" };
greet.call(person1); // "Hello, I'm Priya"
greet.call(person2); // "Hello, I'm Arjun"
.call() takes the object you want as this and passes it as the first argument. The function runs with that object as its context.
.apply() does the same thing but passes additional arguments as an array:
function introduce(city, role) {
console.log(`I'm \({this.name} from \){city}, working as ${role}`);
}
introduce.call({ name: "Zara" }, "Bangalore", "Developer");
// "I'm Zara from Bangalore, working as Developer"
introduce.apply({ name: "Zara" }, ["Bangalore", "Developer"]);
// Same result — just passes args as array
Pattern 4: bind — Fix this Permanently
.bind() creates a new function with this permanently locked to whatever you specify. The original function is unchanged. You get back a new version of it:
const user = {
name: "Priya",
greet: function() {
console.log("Hello, I'm " + this.name);
}
};
const greetPriya = user.greet.bind(user); // lock this to user
greetPriya(); // "Hello, I'm Priya" — works!
greetPriya(); // "Hello, I'm Priya" — always works
Now greetPriya can be called anywhere, passed to event listeners, stored in a variable, and this will always be user.
This solves the event listener problem from earlier:
document.querySelector("button").addEventListener(
"click",
button.handleClick.bind(button) // lock this to button object
);
// "Clicked: Submit" — fixed!
Arrow Functions and this: A Special Case
Arrow functions behave completely differently from regular functions when it comes to this. They don't have their own this. Instead, they inherit this from the surrounding scope, wherever the arrow function was defined.
const timer = {
name: "My Timer",
start: function() {
// 'this' here is the timer object ✓
setTimeout(function() {
console.log(this.name); // undefined — 'this' is window in a regular function
}, 1000);
}
};
timer.start(); // "undefined" after 1 second
The callback passed to setTimeout is a regular function. By the time it runs, this is no longer the timer object, it's window.
Fix it with an arrow function:
const timer = {
name: "My Timer",
start: function() {
// 'this' here is the timer object ✓
setTimeout(() => {
console.log(this.name); // "My Timer" — arrow inherits this from start()
}, 1000);
}
};
timer.start(); // "My Timer" after 1 second
The arrow function doesn't create its own this. It looks outward to the enclosing start function, where this is the timer object.
This is why arrow functions are commonly used for callbacks inside methods, they preserve the this of the surrounding method automatically.
However, don't use arrow functions as methods themselves:
const user = {
name: "Priya",
greet: () => {
console.log("Hello, I'm " + this.name); // undefined — wrong!
}
};
user.greet(); // "Hello, I'm undefined"
When greet is defined as an arrow function at the object literal level, it looks outward to the global scope for this, not to user. this.name becomes window.name, which is undefined.
Rule of thumb: Use regular functions for object methods. Use arrow functions for callbacks inside those methods.
A Summary of All the Rules
How the function is called What `this` is
───────────────────────────────── ──────────────────────────────
obj.method() obj (the object on the left)
fn() window (or undefined in strict)
fn.call(obj, arg1, arg2) obj (you specify it)
fn.apply(obj, [arg1, arg2]) obj (you specify it)
fn.bind(obj) obj (locked permanently)
Arrow function () => {} Inherits from surrounding scope
Key Takeaways
thisrefers to the calling context, the object that called the function, not where the function was written.In the global context,
thisiswindow(orundefinedin strict mode).In an object method called with
obj.method(),thisisobj.In a plain function call,
thisdefaults towindow(orundefinedin strict mode).Pulling a method out of an object and calling it separately loses the original
this— a very common bug..call()and.apply()let you choose whatthisis for a single call..bind()creates a new function withthispermanently fixed to a specific object.Arrow functions don't have their own
this, they inherit it from their surrounding scope.Use regular functions for object methods. Use arrow functions for callbacks inside those methods.
Quick Reference
// Method call — this is the object
obj.greet(); // this = obj
// Plain call — this is global or undefined
greet(); // this = window / undefined
// call — manually set this (args one by one)
fn.call(obj, a, b);
// apply — manually set this (args as array)
fn.apply(obj, [a, b]);
// bind — create new function with fixed this
const bound = fn.bind(obj);
bound(); // this = obj, always
// Arrow function — inherits this from outer scope
const obj = {
name: "Priya",
start() {
setTimeout(() => {
console.log(this.name); // "Priya" — inherited from start()
}, 1000);
}
};
// Common mistake — arrow as a method
const obj = {
greet: () => { console.log(this); } // this = window — not obj!
};
this is never about where a function lives. It's always about who called it. Once that lands, the rest is just details.

