Deciding this: A Priority List

JavaScript's this has a reputation for being confusing, but the rules behind its value are actually finite and deterministic. The value is decided by a strict priority order. To figure out what this will be in a given function, check the conditions below in order and stop at the first one that matches.

  1. If the function is defined as an arrow function.
  2. Otherwise, if the function/class is called with new.
  3. Otherwise, if the function has a "bound" this value.
  4. Otherwise, if this is set at call-time via call or apply.
  5. Otherwise, if the function is called via a parent object (parent.func()).
  6. Otherwise, if the function or its parent scope is in strict mode.
  7. Otherwise, this equals globalThis.

Priority 1: Arrow Functions

Arrow functions do not have their own this. The value of this inside them is always the value of this in the enclosing (parent) scope.

Because the value is inherited, it cannot be overridden by any call-time technique:

  • It cannot be changed with bind.
  • It cannot be changed with call or apply.
  • It cannot be changed by calling the function as a method of another object.
  • It cannot be changed by invoking the arrow function as a constructor.

This immutability makes arrow functions especially useful for instance methods where you want this to always refer to the class instance. The modern pattern is to combine arrow functions with class fields, which are effectively syntactic sugar for assigning the function in the constructor:

Ensuring this in Class Methods

If you cannot use class fields, a reasonable alternative is to assign the function inside the constructor. This avoids the common failure mode where an instance method is passed around or used as an event listener and loses its connection to the object.

Priority 2: Called with new

When a function or class is invoked with the new keyword, the runtime creates a new object via Object.create(Constructor.prototype) and sets this to that new object for the duration of the call.

This priority is high: invoking a bound function with new will still create a new object, ignoring the bound this. Similarly, calling a constructible function as a member of a parent object does not affect the this value when it is invoked with new.

Priority 3: Bound Functions

If a function is created using Function.prototype.bind(), the resulting "bound" function will always use the object passed to bind as its this value, no matter how the bound function is subsequently invoked.

Neither call/apply nor invocation as a member of another object can change the this of a bound function.

Priority 4: call and apply

You can explicitly set this at call-time using Function.prototype.call() or Function.prototype.apply(). In these cases, the object passed as the first argument becomes this inside the function.

A Caution on Implicit this

Some APIs, particularly DOM event listeners, will set this on your behalf to some other value. This can lead to code that is difficult to reason about. A common pitfall is relying on this for event handling logic. To keep intent explicit and avoid coupling your logic to how the environment chooses to set this, it's often clearer to replace a this-based implementation with a closure that references the target object (or value) directly.

Priority 5: Parent Object Invocation

When a function is called as a member of an object, as in obj.method(), this inside that function will refer to obj.

The connection is made at the moment of the call, which means it breaks if the method is extracted and called standalone:

This is a classic source of bugs when developers try to pass a method as a callback. For example, taking a method off one object and passing it to another object's method (like querySelector) often fails, because the receiving method inspects its own this value and expects it to point to a valid object of a certain type.

An interesting exception is the console API. Console methods like log do not use internal this references, which allows them to be extracted and called standalone without binding.

Priority 6: Strict Mode

If none of the previous conditions apply and the function (or its enclosing scope) is in strict mode, this will be undefined. A 'use strict' directive is only required in non-module scripts; all ES modules are automatically strict.

Priority 7: The Fallback

In the last case—a regular, non-strict function called without any of the above context—this will be the global object (globalThis).