Q: How does the this keyword work in JavaScript? Explain all the binding rules.
Answer:
this is not a reference to the function itself or to where it was defined. It is determined by how a function is called (call-site), with four classic rules plus arrow-function lexical binding. Strict mode and ES modules also affect the defaults.
The Four Classic Rules (in precedence order)
newbinding —new Foo()creates a new object and bindsthisto it.- Explicit binding —
fn.call(obj),fn.apply(obj),fn.bind(obj). - Implicit binding —
obj.fn()bindsthistoobj. - Default binding — bare
fn()call. In strict modethisisundefined; in sloppy mode it's the global object (window/globalThis).
Arrow functions ignore all four rules: they capture this from the enclosing lexical scope at definition time.
Examples for Each Rule
'use strict';
function whoAmI() { return this; }
// 4. Default
whoAmI(); // undefined (strict) / window (sloppy)
// 3. Implicit
const obj = { whoAmI };
obj.whoAmI(); // obj
// 2. Explicit
whoAmI.call({ name: 'X' }); // { name: 'X' }
// 1. new
function Person(name) { this.name = name; }
const p = new Person('Ada'); // this -> brand new object
The "Lost this" Pitfall
const user = {
name: 'Ada',
greet() { console.log(`Hi, ${this.name}`); },
};
const greet = user.greet;
greet(); // Hi, undefined (default binding!)
setTimeout(user.greet, 100); // same problem — method passed as bare reference
The reference to the function was extracted from user, so the call-site is now plain greet(). Fixes:
setTimeout(user.greet.bind(user), 100);
setTimeout(() => user.greet(), 100); // arrow keeps `user.greet()` as the call-site
Arrow Functions
Arrows don't have their own this, arguments, super, or new.target. They inherit this from the surrounding scope at creation time — bind/call/apply cannot change it.
class Timer {
constructor() {
this.count = 0;
setInterval(() => { this.count++; }, 1000); // `this` is the instance
}
}
If you used a function expression here, the interval callback would have this === undefined (strict) or window.
Precedence Quick Test
function Foo() { this.a = 1; }
const obj = {};
const bound = Foo.bind(obj);
new bound(); // {a: 1} — `new` wins over bind
console.log(obj.a); // undefined
[!NOTE]
newbeats explicit binding. This is the only case wherebindcan be overridden.
Method Shorthand vs Property Arrow in Classes
class Btn {
// Prototype method — `this` is whoever calls it
click() { console.log(this); }
// Class field arrow — `this` is the instance permanently
onClick = () => console.log(this);
}
Use the arrow-field form for event handlers passed to React/DOM to avoid manual .bind.
Modules and this
At the top level of an ES module, this is undefined. In a CommonJS module, top-level this equals module.exports. In a browser <script> without type=module, top-level this is window.