How does the this keyword work in JavaScript?
Advertisement
🧩 Scenario
Architecture Walkthrough
The Four Binding Rules
JavaScript determines this by four rules applied in order of precedence from lowest to highest. The default binding applies when a function is called with no receiver: in non-strict mode, this is the global object (window in browsers); in strict mode, this is undefined. Implicit binding applies when a function is called as a method of an object: obj.method() sets this to obj.
Explicit binding applies when call, apply, or bind is used to specify this directly. new binding applies when a function is called with the new keyword: a new empty object is created, this inside the constructor refers to it, and the new object is returned implicitly unless the constructor explicitly returns a different object.
Losing this When Extracting Methods
The most common this bug occurs when a method is extracted from an object and called without the object as the receiver. The invocation context determines this, not the definition context. Once the method reference is stored in a standalone variable or passed as a callback, the link to the original object is broken.
Three fixes are available: call bind(this) to create a permanently bound function, wrap the call in an arrow function that closes over the correct this, or use a class field arrow function which captures this at class instantiation time rather than at call time. Each approach has different readability and performance implications.
Arrow Functions and Lexical this
Arrow functions do not have their own this binding. They inherit this from the lexical scope where they are defined, not from where they are called. This makes them predictable for callbacks inside class methods or factory functions: whatever this was in the surrounding scope when the arrow function was defined is what this will always be inside it.
The consequence is that arrow functions cannot be used as constructors (new arrowFn() throws), and call, apply, or bind has no effect on this inside an arrow function. The first argument to these methods is silently ignored.
Key Code Explained
// Default binding
function show() {
console.log(this);
}
show(); // window (non-strict) or undefined (strict)
// Implicit binding
const user = {
name: 'Ghazi',
greet() { console.log(this.name); },
};
user.greet(); // 'Ghazi' — this is user
// Losing this: extraction breaks implicit binding
const greet = user.greet;
greet(); // undefined (strict mode) — this is no longer user
// Fix 1: bind
const boundGreet = user.greet.bind(user);
boundGreet(); // 'Ghazi'
// Fix 2: arrow wrapper
setTimeout(() => user.greet(), 1000); // 'Ghazi' — closure over user
// Explicit binding
function introduce(role) {
console.log(`${this.name} is a ${role}`);
}
introduce.call(user, 'developer'); // 'Ghazi is a developer'
// new binding
function Person(name) {
this.name = name; // this is the new object
}
const p = new Person('Ghazi');
console.log(p.name); // 'Ghazi'
// Arrow function: lexical this, cannot be rebound
const obj = {
name: 'Obj',
regular() { console.log(this.name); },
arrow: () => { console.log(this.name); }, // this is outer scope at definition
};
obj.regular(); // 'Obj'
obj.arrow(); // undefined — this is lexical, not obj
The extraction example is the most important: const greet = user.greet followed by greet() loses the implicit binding to user. The function runs in default mode, so this becomes undefined in strict mode. The three fixes demonstrate the range of solutions available depending on whether you need a one-time binding, a reusable wrapper, or a class-level fix.
Tradeoffs
| Binding approach | When this is set | Rebindable |
|---|---|---|
| Default (standalone call) | At call time, to global or undefined | Not applicable |
| Implicit (method call) | At call time, to the calling object | Yes, by changing the caller |
| Explicit (call/apply/bind) | Forced at call time or permanently | Yes, except on arrow functions |
| new | At construction time, to new object | No |
| Arrow function (lexical) | At definition time, from surrounding scope | No, call/apply/bind have no effect |
What Interviewers Actually Check
- Whether you can name and rank all four binding rules correctly
- Whether you can predict
thisin a standalone call, a method call, anewcall, and an arrow function - Whether you understand why extracting a method and calling it as a function loses
this - Whether you can describe at least two ways to fix a lost
thiscontext - Whether you know that arrow functions capture
thislexically and cannot be rebound
Follow-Up Questions
- What does
thisrefer to inside aclassstatic method? - If a constructor function explicitly returns an object, what does
newreturn? - How do class field arrow functions differ from binding in the constructor in terms of memory usage?
- What is the value of
thisinside a method called in anArray.prototype.forEachcallback written as a regular function? - A teammate says "just use arrow functions everywhere to avoid
thisbugs." Where does that advice break down?
Common Candidate Mistakes
- Assuming
thisinside a method still refers to the object after the method is extracted and called as a standalone function - Thinking arrow functions can be rebound with
callorbindwhen they cannot - Not knowing that a regular function called without a receiver uses
undefinedforthisin strict mode, not the global object - Confusing
thisinside a nested regular function inside an object method, which defaults toundefinedin strict mode - Saying "arrow functions always work for
this" without knowing they break when used as object methods
Interview Readiness Checklist
Before you leave this question, make sure you can answer:
- Can you name all four
thisbinding rules and rank them by precedence? - Can you predict what
thisis in a standalone function call, a method call, anewcall, and an arrow function? - Can you explain why extracting a method and calling it as a standalone function loses its original
this? - Can you fix a lost
thiscontext usingbind, an arrow function wrapper, or a stored reference? - Can you explain what
newdoes tothisinside a constructor function?
Summary
The value of this in JavaScript is determined by how a function is invoked, not where it is defined. Four rules apply in order of precedence: default binding (standalone call, this is global or undefined), implicit binding (method call, this is the calling object), explicit binding (call/apply/bind, this is specified), and new binding (constructor call, this is the new object).
Arrow functions break this pattern entirely. They do not have their own this. They capture this lexically from their surrounding scope at the time they are defined, and no binding mechanism can change it.
The most common bug is losing implicit binding when a method is extracted and called without its object. The fix is either bind for a permanent binding, an arrow wrapper for a one-time call, or class field syntax for a class-level permanent solution. Knowing these four rules and their precedence covers the vast majority of this questions in any JavaScript interview.
Does "this" refer to the function itself?
No. It refers to the object that invoked the function, or is determined by call-time context depending on how the function is called.
Advertisement
Stay Updated
Get the latest frontend challenges, interview questions and tutorials delivered to your inbox.
Advertisement