Four rules determine this, with this priority order: new > explicit > implicit > default
// 1. Default binding — standalone function call
function fn() { console.log(this); }
fn(); // global object (window) in sloppy mode, undefined in strict
// 2. Implicit binding — method call (object before the dot)
const obj = { name: 'Alice', fn() { return this.name; } };
obj.fn(); // 'Alice' — this = obj
// ⚠️ Implicit binding LOST on assignment
const fn2 = obj.fn;
fn2(); // undefined — no object before dot
// 3. Explicit binding — call, apply, bind
function greet(greeting) { return `${greeting}, ${this.name}`; }
greet.call({ name: 'Bob' }, 'Hello'); // 'Hello, Bob'
greet.apply({ name: 'Carol' }, ['Hi']); // 'Hi, Carol'
const bound = greet.bind({ name: 'Dave' });
bound('Hey'); // 'Hey, Dave' — permanently bound
// 4. new binding — constructor call
function Person(name) { this.name = name; }
const p = new Person('Eve');
p.name; // 'Eve' — this = freshly created object
// new: 1) creates {} 2) links prototype 3) binds this 4) returns it
// Arrow functions: LEXICAL this — none of the 4 rules apply
const obj2 = {
name: 'Zara',
fn: () => this.name, // this = outer scope (NOT obj2)
method() { return () => this.name; } // nested arrow captures method's this
};
obj2.fn(); // undefined — arrow ignores implicit binding
obj2.method()(); // 'Zara' — arrow captured method's this
💡 Interview rule: ask "How was the function called?" Default → standalone call. Implicit → object.method(). Explicit → call/apply/bind. new → constructor. Arrow → look where it was DEFINED.