this Binding Rules
How JavaScript determines 'this' at call time β the 4 binding rules, arrow functions, and why methods lose their context.
In JavaScript, 'this' is not determined by where a function is defined, but by HOW it is called. There are 4 binding rules with a strict priority order. Arrow functions are the exception β they capture 'this' lexically from their enclosing scope.
Unlike most languages where 'this' is determined by where the method is defined, JavaScript's 'this' depends on HOW the function is called. The same function can have different 'this' values depending on the call site.
1) new Binding (highest): new Foo() β this = fresh object. 2) Explicit: call/apply/bind β this = specified object. 3) Implicit: obj.method() β this = obj. 4) Default (lowest): solo() β this = globalThis or undefined (strict).
Arrow functions don't have their own 'this'. They capture 'this' from the enclosing scope at DEFINITION time (not call time). This makes them ideal for callbacks and event handlers where you want to preserve the outer 'this'.
When you extract a method (const fn = obj.method), the implicit binding is lost. fn() is now a bare function call β default binding. This is why React class component methods need .bind(this) in the constructor.
Key Concepts
The location in code where a function is called. It determines 'this' by applying the 4 binding rules. Not where the function is defined.
Creates a new function with 'this' permanently set. Cannot be overridden by further bind/call/apply (hard binding).
The global 'this' value: 'window' in browsers, 'global' in Node.js, 'self' in workers. Standardized in ES2020.
In strict mode, default binding gives this = undefined instead of globalThis. This prevents accidentally polluting the global object.
1// Priority: new > explicit > implicit > default23// Implicit binding β object before the dot:4const user = {5 name: "Alice",6 greet() { return "Hi, " + this.name; }7};8user.greet(); // "Hi, Alice"910// Implicit binding LOST:11const fn = user.greet; // extracted method12fn(); // "Hi, undefined" β this is now global/undefined!1314// Fix with bind:15const bound = user.greet.bind(user);16bound(); // "Hi, Alice"1718// Arrow function β lexical this:19class Timer {20 seconds = 0;21 start() {22 setInterval(() => {23 this.seconds++; // β 'this' is the Timer instance24 }, 1000);25 }26}
'this' is one of the most confusing parts of JavaScript. Understanding the 4 binding rules eliminates an entire class of bugs: lost context in callbacks, event handlers, class methods, and setTimeout. It also explains why arrow functions and bind() exist.
Common Pitfalls
1React Class Component Methods
Your React class component's onClick handler logs 'Cannot read property name of undefined'. The method works when called directly but breaks when passed as an event handler.
Passing this.handleClick to onClick extracts the method, losing the implicit binding. When React calls the handler, 'this' is undefined (React uses strict mode internally).
Three options: 1) Bind in constructor: this.handleClick = this.handleClick.bind(this). 2) Class field with arrow function: handleClick = () => { ... }. 3) Use functional components with hooks (eliminates 'this' entirely).
Takeaway: This is why React moved to functional components. Hooks eliminate 'this' binding confusion by using closures instead. If you maintain class components, always bind or use arrow function class fields.
2Event Listener Context
Your vanilla JS code attaches obj.handleClick as a DOM event listener. Inside the handler, 'this' points to the DOM element, not your object.
addEventListener sets 'this' to the element that triggered the event (implicit binding by the browser). Your object's properties are inaccessible via 'this'.
Use bind: el.addEventListener('click', obj.handleClick.bind(obj)). Or use an arrow function wrapper: el.addEventListener('click', () => obj.handleClick()). For removal, store the bound reference.
Takeaway: Browser APIs rebind 'this' to the DOM element. This is by design β it's useful for event delegation. But if you need your object's context, you must explicitly preserve it.