JavaScript this Table

Contextthis isField note
obj.method()objThe object before the dot - call-site decides
method() aloneundefined (strict) / global (sloppy)The dot was lost - the classic detach bug
new Class()The new instanceConstructor context; this IS the object being built
arrow functionInherited from enclosing scopeNo own this ever - defined at creation, immune to call()
fn.call(obj) / applyobj explicitlycall takes args one by one; apply takes an array
fn.bind(obj)obj permanentlyReturns a bound copy; cannot be re-bound
event handlerThe elementaddEventListener sets this to the currentTarget
global scopeundefined (strict) / window (sloppy)Module code is always strict - this is undefined there
Reference: the MDN this reference. 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 definition. The classic bug: extracting a method to a variable detaches it from its object, and this falls back to undefined (strict) or global. Bottom line: class methods and regular functions get this from the call site; arrows get it from the enclosing scope; bind/call/apply override deliberately. Related tools: closures table (lexical scope), classes table (this in constructors and methods), and error table for the TypeErrors detached methods produce.

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

  1. Diagnose by the call site: what object is before the dot (or before the parenthesis)? That is this for regular functions.
  2. Reach for bind/call/apply when detaching is intentional: callbacks, event handlers, partial application.
  3. 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.

Related tools