Written by Rahul · Frontend Engineer at Google · Updated 2025
I'm going to be real with you — this in JavaScript confused me for years. Even after working at Google for 3 years, I still occasionally get tripped up. The problem isn't that this is complicated. The problem is that people try to memorize rules instead of understanding the one simple principle: this is determined by how a function is called, not where it's defined.
The 5 Rules of "this" — In Order of Priority
Rule 1: new Binding (Highest Priority)
function User(name) {
this.name = name; // "this" = the newly created object
}
const rahul = new User("Rahul");
console.log(rahul.name); // "Rahul"When you use new, JavaScript creates a fresh object and sets this to that object. Always.
Rule 2: Explicit Binding — call, apply, bind
function greet() {
console.log(`Hello, ${this.name}`);
}
const user = { name: "Rahul" };
greet.call(user); // "Hello, Rahul"
greet.apply(user); // "Hello, Rahul"
const bound = greet.bind(user);
bound(); // "Hello, Rahul"call and apply invoke immediately. bind returns a new function with this permanently set.
Rule 3: Implicit Binding — Object Method
const user = {
name: "Rahul",
greet() {
console.log(`Hello, ${this.name}`);
}
};
user.greet(); // "Hello, Rahul" — this = user (the object before the dot)The object before the dot becomes this. Simple.
Rule 4: Default Binding — Standalone Function
function showThis() {
console.log(this);
}
showThis(); // window (browser) or global (Node.js)
// In strict mode: undefinedRule 5: Arrow Functions — Lexical "this"
const user = {
name: "Rahul",
greet: () => {
console.log(this.name); // ❌ undefined! Arrow functions don't have their own "this"
},
greetCorrect() {
const inner = () => {
console.log(this.name); // ✅ "Rahul" — inherits from greetCorrect
};
inner();
}
};Arrow functions don't have their own this. They inherit this from the enclosing scope at definition time.
The Classic Pitfall — Losing "this" in Callbacks
This is the #1 bug I see in React codebases:
class Button extends React.Component {
constructor() {
super();
this.state = { count: 0 };
}
handleClick() {
// ❌ "this" is undefined here when used as callback!
this.setState({ count: this.state.count + 1 });
}
render() {
// The problem: handleClick is passed as a callback
// When React calls it, there's no object before the dot
return <button onClick={this.handleClick}>Click</button>;
}
}Three fixes:
// Fix 1: Bind in constructor
constructor() {
this.handleClick = this.handleClick.bind(this);
}
// Fix 2: Arrow function in class field (most common)
handleClick = () => {
this.setState({ count: this.state.count + 1 });
};
// Fix 3: Arrow function in JSX (creates new function each render — avoid)
<button onClick={() => this.handleClick()}>Click</button>Real Production Issues
Issue 1: Event Handler "this" in Vanilla JS
Issue 2: setTimeout Losing Context
Issue 3: Destructured Methods
Best Practices
- Use arrow functions for callbacks — they inherit
thisand prevent 90% of bugs - In React, use hooks — functional components with hooks eliminate
thisentirely - Never rely on default binding — always use strict mode (
"use strict") - Use arrow functions in class fields for methods that will be passed as callbacks
- When in doubt,
console.log(this)— seriously, just log it
Things to Avoid
- ❌ Don't use arrow functions as object methods (they won't have the right
this) - ❌ Don't use
.bind()in render methods (creates new function every render) - ❌ Don't mix
thispatterns — pick one approach per codebase
Quick Reference
| Call Style | "this" Points To |
|---|---|
new Foo() | New object |
foo.call(obj) / foo.apply(obj) | obj |
obj.foo() | obj |
foo() | window / undefined (strict) |
() => {} | Enclosing scope's this |