How Does the `this` Keyword Work in JavaScript?
Learn how JavaScript resolves `this` by call site, why arrow functions differ, and how to avoid the most common `this` bugs.
Expected Interview Answer
The value of `this` in JavaScript is not fixed by where a function is defined but by how that function is called at runtime — its binding is determined by the call site, except for arrow functions, which inherit `this` lexically from their enclosing scope.
When a function is called as a method (`obj.method()`), `this` binds to the object before the dot. When called as a plain function (`fn()`), `this` is `undefined` in strict mode or the global object in sloppy mode. When called with `new`, `this` is a freshly created object linked to the constructor prototype. When called via `call`, `apply`, or `bind`, `this` is explicitly set to whatever is passed as the first argument. Arrow functions break this pattern entirely: they have no own `this` binding and instead capture `this` lexically from the surrounding code at the time they are defined, which is why they are commonly used inside callbacks to preserve the outer `this`. Losing track of the call site — for example, passing a method as a bare callback — is the single most common source of `this` bugs.
- Enables flexible, reusable method definitions independent of a fixed object
- Arrow functions solve the classic callback `this`-loss problem cleanly
- call/apply/bind give explicit, deliberate control over binding
- Understanding call-site rules eliminates an entire class of runtime bugs
AI Mentor Explanation
The word “captain” refers to whoever is currently leading the side, not a fixed person tied to the word itself — swap the leader before the toss and “captain” now means someone new. That is exactly how `this` resolves: it depends on who is standing at the call site when the function runs, not on where the function was written. A team announcer using a fixed script (an arrow function) instead keeps referring to whichever captain was named when the script was written, ignoring later changes. This call-site-versus-lexical distinction is the whole story of `this` binding.
Step-by-Step Explanation
Step 1
Find the call site
Look at how the function is actually invoked, not where it was defined.
Step 2
Apply the binding rules in order
Check `new` binding, then explicit call/apply/bind binding, then implicit method-call binding, then default binding.
Step 3
Special-case arrow functions
Arrow functions ignore all call-site rules and inherit `this` lexically from the enclosing scope.
Step 4
Verify against strict mode
Default binding gives `undefined` in strict mode versus the global object in sloppy mode.
What Interviewer Expects
- Correctly explains call-site-based resolution, not definition-site
- Distinguishes implicit, explicit, new, and default binding
- Explains why arrow functions behave differently (lexical `this`)
- Can identify `this` bugs in a bare callback / detached method example
Common Mistakes
- Claiming `this` depends on where a function is defined (true only for arrow functions)
- Forgetting that passing a method as a callback detaches it from its object
- Assuming `this` is always the global object regardless of strict mode
- Confusing arrow function lexical `this` with regular function default binding
Best Answer (HR Friendly)
“In JavaScript, `this` depends on how a function gets called, not where it was written. If you call it as `obj.method()`, `this` is `obj`. If you just pass the function around and call it plain, you can lose that connection — that is a classic bug. Arrow functions solve this by always using the `this` from the surrounding code, which is why they are so common in callbacks.”
Code Example
const user = {
name: 'Ava',
greetRegular: function () {
console.log(`Hi, I’m ${this.name}`) // 'this' = whoever calls it
},
greetArrow: () => {
console.log(`Hi, I’m ${this.name}`) // 'this' = enclosing scope, NOT user
},
}
user.greetRegular() // "Hi, I’m Ava" (method call binding)
const detached = user.greetRegular
detached() // "Hi, I’m undefined" (default binding, lost the object)
const bound = user.greetRegular.bind(user)
bound() // "Hi, I’m Ava" (explicit binding restores it)Follow-up Questions
- Why does a method passed as an event handler often lose its `this` binding?
- How does `bind` differ from `call` and `apply`?
- What is `this` inside a class constructor versus a class method used as a callback?
- How does `this` behave inside a regular function nested inside an arrow function?
MCQ Practice
1. What determines the value of `this` inside a regular (non-arrow) function?
`this` for regular functions is resolved dynamically based on the call site, not lexical position.
2. How does an arrow function determine `this`?
Arrow functions have no own `this`; they capture it lexically from where they were defined.
3. What happens to `this` when a method is passed as a bare callback (e.g., `setTimeout(obj.method)`)?
Detaching a method from its object strips the implicit binding, falling back to default binding.
Flash Cards
What decides `this` in a regular function? — The call site — how the function is invoked, not where it was written.
How does an arrow function get `this`? — Lexically, inherited from the enclosing scope at definition time.
What is default binding? — `this` falls back to `undefined` (strict mode) or the global object (sloppy mode).
How do you force a specific `this`? — Use `call`, `apply`, or `bind` to explicitly set it.