How does the this keyword work in JavaScript?
this is decided by how a function is called, not where it is written. A method call obj.fn() sets this to obj; new sets it to the new object; call, apply and bind set it explicitly; a plain call gives undefined in strict mode. Arrow functions have no own this and use the surrounding one.
Explain it simply
The word "this" in a sentence depends on who is speaking. If Rohit says "this bat is mine", it is Rohit's bat. If Virat says the same sentence, it is Virat's bat. The sentence is the same; the speaker changes the meaning.
In JavaScript, a normal function is the sentence, and this is the speaker. It is decided at the moment of the call, by looking at how the function was called. The same function can have a different this every time.
Arrow functions are the exception. They do not have their own this. They borrow this from the code around them, like repeating what the person next to you said.
Example
const user = {
name: "Priya",
greet() {
return "Hi, " + this.name;
},
};
console.log(user.greet()); // Output: Hi, Priya
console.log(user.greet.call({ name: "Kiran" })); // Output: Hi, Kiran
const greetRahul = user.greet.bind({ name: "Rahul" });
console.log(greetRahul()); // Output: Hi, Rahul
const team = {
name: "CSK",
players: ["Dhoni", "Jadeja"],
list() {
// The arrow function uses list()'s this, which is team.
return this.players.map((p) => p + " plays for " + this.name);
},
};
console.log(team.list()); // Output: ["Dhoni plays for CSK","Jadeja plays for CSK"]
class Wallet {
constructor(balance) {
this.balance = balance; // with new, this is the new object
}
}
console.log(new Wallet(500).balance); // Output: 500The classic bug, losing this:
const greet = user.greet; // just the function, no object attached
greet(); // in strict mode or a class: this is undefined -> TypeErrorHow it works
Check these rules in order:
- Called with
new?thisis the newly created object. - Called with
call,applyorbind?thisis the object you passed. - Called as a method,
obj.fn()?thisisobj, the thing before the dot. - Plain call,
fn()?thisisundefinedin strict mode (modules and classes are always strict). In old sloppy-mode scripts it is the global object.
Arrow functions skip all four rules. Their this is fixed to whatever this was where the arrow was created, and call, apply and bind cannot change it.
Real-world use
- Passing a method as a callback, like
button.addEventListener("click", cart.checkout), losesthis. Fix it withcart.checkout.bind(cart)or an arrow wrapper() => cart.checkout(). - In DOM event handlers written as normal functions,
thisis the element the listener is attached to. - In React class components, methods were bound in the constructor for this exact reason.
Common mistakes
- Using an arrow function as an object method.
thiswill not be the object. - Thinking
thisrefers to the function itself or to where the function was written. - Extracting a method (
const fn = obj.fn) and calling it alone. - Expecting
bindto work on an arrow function.
What to say in the interview
"For normal functions, this is set by the call site. With new it is the new object. With call, apply or bind it is whatever I pass. With a method call like obj.fn() it is obj. With a plain function call it is undefined in strict mode. Arrow functions do not have their own this; they capture it from the surrounding scope, which is why they are handy for callbacks inside methods but a bad choice for object methods. Most this bugs come from passing a method as a callback, which I fix with bind or an arrow wrapper."
Follow-up questions
- What is the difference between
call,applyandbind? - What does
thisrefer to inside asetTimeoutcallback? - Why should you not use arrow functions as object methods?
Go deeper
Read these once the basics above feel easy.
call(thisArg, a, b) and apply(thisArg, [a, b]) invoke the function immediately and differ only in how arguments are passed. bind(thisArg) returns a new function with this permanently fixed, even if it is later called as a method of another object. A bound function can still be used with new, and in that case the new object wins over the bound this. Class fields with arrow functions, such as handleClick = () => { ... }, create one bound function per instance, which is convenient but uses a little more memory than a shared prototype method.