this is one of the most confusing parts of JavaScript, and it comes up in almost every OOP interview. The key thing to understand: this is NOT fixed when we write the code — it’s determined when the function is called. The same function can have different this values depending on how we call it.
The Rules of this
There are a few simple rules. Once we know them, this stops being mysterious.
this determined?this from surrounding scope (lexical). Can't be changed.this = the newly created object.this = whatever we explicitly pass in.this = the object before the dot.this = undefined (strict mode) or window (sloppy mode).this in Global Context
In the global scope (outside any function), this refers to the global object — window in browsers, globalThis everywhere.
console.log(this === window); // true (in a browser)
this in Regular Functions
For a regular function, this depends on how it’s called, not where it’s written.
function showThis() {
console.log(this);
}
showThis(); // window (sloppy mode) or undefined (strict mode)
const obj = { name: "Manish", showThis };
obj.showThis(); // { name: "Manish", showThis: f } — the object before the dot
The same function, two different this values. That’s the core idea.
this in Object Methods
When we call a method on an object, this is the object that owns the method — the thing before the dot.
const user = {
name: "Manish",
greet() {
console.log(`Hi, I'm ${this.name}`);
}
};
user.greet(); // Hi, I'm Manish — this = user
this in Constructors
When we use new, JavaScript creates a fresh empty object and sets this to point to it.
function Person(name) {
this.name = name; // this = the new object being created
}
const p = new Person("Manish");
console.log(p.name); // "Manish"
Arrow Functions & Lexical this
Arrow functions don’t get their own this. They capture this from wherever they were defined. This is called lexical this and it’s one of the biggest reasons arrow functions exist.
const team = {
name: "Avengers",
members: ["Tony", "Steve"],
printMembers() {
this.members.forEach((member) => {
console.log(`${member} is in ${this.name}`); // this = team (from printMembers)
});
}
};
team.printMembers(); // Tony is in Avengers, Steve is in Avengers
If we used a regular function inside forEach, this would be undefined (strict mode) instead of team. Arrow functions save us here.
call(), apply(), and bind()
These let us explicitly set this:
call(thisArg, arg1, arg2, ...)— calls the function immediately with the giventhisapply(thisArg, [argsArray])— same ascall, but arguments are an arraybind(thisArg)— returns a new function withthispermanently set
function greet(greeting) {
console.log(`${greeting}, I'm ${this.name}`);
}
const user = { name: "Manish" };
greet.call(user, "Hey"); // Hey, I'm Manish
greet.apply(user, ["Hey"]); // Hey, I'm Manish
const boundGreet = greet.bind(user);
boundGreet("Hey"); // Hey, I'm Manish
The only difference between call and apply is how we pass arguments — individually vs as an array.
this in Event Handlers
In DOM event handlers, this is the element that the listener is attached to.
button.addEventListener("click", function () {
console.log(this); // the button element
this.style.color = "red";
});
// But with arrow function:
button.addEventListener("click", () => {
console.log(this); // NOT the button — it's the outer this (probably window)
});
This is one case where we actually want a regular function, not an arrow function.
The Classic Pitfall: Losing this
The most common bug with this happens when we pass a method as a callback. The method gets detached from its object and this becomes undefined.
class Timer {
constructor() {
this.seconds = 0;
}
start() {
// BUG: regular function loses this
// setInterval(function() { this.seconds++; }, 1000); // this = undefined!
// FIX 1: arrow function (inherits this from start())
setInterval(() => { this.seconds++; }, 1000);
// FIX 2: bind
// setInterval(function() { this.seconds++; }.bind(this), 1000);
}
}
Whenever we see this inside a callback and it’s not working, the first thing to check is whether we’re using an arrow function or need to .bind(this).
this in Classes
Class methods behave like object methods — this is the instance. But the same “losing this” problem applies if we pass a method as a callback.
class Logger {
prefix = "[LOG]";
log(msg) {
console.log(`${this.prefix} ${msg}`);
}
}
const logger = new Logger();
logger.log("hello"); // [LOG] hello
const fn = logger.log;
// fn("hello"); // TypeError: Cannot read properties of undefined
const boundFn = logger.log.bind(logger);
boundFn("hello"); // [LOG] hello
In simple language, this in JavaScript isn’t about where we write the function — it’s about how we call it. Arrow functions are the exception because they lock in this from their surrounding scope. When in doubt, check the call site or use .bind().