JavaScript this Table
| Context | this is | Field note |
|---|---|---|
obj.method() | obj | The object before the dot - call-site decides |
method() alone | undefined (strict) / global (sloppy) | The dot was lost - the classic detach bug |
new Class() | The new instance | Constructor context; this IS the object being built |
arrow function | Inherited from enclosing scope | No own this ever - defined at creation, immune to call() |
fn.call(obj) / apply | obj explicitly | call takes args one by one; apply takes an array |
fn.bind(obj) | obj permanently | Returns a bound copy; cannot be re-bound |
event handler | The element | addEventListener sets this to the currentTarget |
global scope | undefined (strict) / window (sloppy) | Module code is always strict - this is undefined there |
this is decided by HOW a function is CALLED, not where it was defined - with the single exception of arrow functions, which capture this lexically at creation. The table below is the working eight: every context that assigns this, from the call-site object to the explicitly bound forms, with the detached-method bug that starts every debugging session.
Bottom line: the call-site decides. obj.method() gives this as obj; pulling method out and calling it alone gives undefined (strict) or the global (sloppy) - the dot was the binding. Arrow functions opt out of the whole system: their this is inherited from the enclosing scope at creation and cannot be changed by any call pattern.
The honest part: the explicit overrides exist for the cases the call-site cannot express. fn.call(obj, a) invokes with this as obj and arguments inline; fn.apply(obj, [a]) same with an array; fn.bind(obj) returns a permanently-bound copy. And in classes, this before super() is a ReferenceError - the language protecting you from reading an object that construction has not built yet.
How to use
- Diagnose by the call site: what object is before the dot (or before the parenthesis)? That is this for regular functions.
- Reach for bind/call/apply when detaching is intentional: callbacks, event handlers, partial application.
- Use arrow functions when this should stay lexical - the callback inside a method that needs the method's this.
Frequently asked questions
Why does my method lose this when I assign it to a variable?
Because this is decided at the CALL SITE, and the call site changed: const detached = obj.method; detached() calls the function with no object before the dot, so this becomes undefined (strict mode) or the global object. The classic cases: setTimeout(obj.method, 100), passing a method as a callback, or aliasing in a variable. Fixes: an arrow wrapper (() => obj.method()), bind(obj) for a permanent fix, or the class-field arrow syntax that binds at definition.
How does this work inside arrow functions?
Arrows have NO own this at all - this inside an arrow is the same this as the enclosing scope, captured at definition and immune to call/apply/bind. This is the feature: a callback arrow inside a method sees the method's this without .bind gymnastics. The flip: an arrow defined at module top level has the module's this (undefined in modules), and no call-site pattern can ever change it - which is exactly what you want for callbacks.
What does this mean in strict vs sloppy mode?
In sloppy mode (no 'use strict'), a function called without a receiver gets the global object (window in browsers) as this - the source of silent global pollution when you assign this.x by mistake. Strict mode makes it undefined instead: the same detached call now throws when you touch this, converting the silent bug into a loud error. Modules and class bodies are always strict, which is why this behaves predictably there.
How do call, apply and bind differ?
All three set this explicitly. call(obj, a, b) invokes immediately with arguments listed one by one; apply(obj, [a, b]) invokes immediately with arguments as an array; bind(obj) does NOT invoke - it returns a new function permanently bound to obj (with optionally pre-filled leading arguments, the partial-application trick). The mnemonic: call is comma-separated, apply is array, bind is a bond that holds.