Skip to content
C Codeloom
JavaScript

The JavaScript this Keyword Explained

Demystify the JavaScript this keyword — understand how it changes by call site, why arrow functions differ, and how to bind context without surprises.

·4 min read · By Codeloom
Beginner 9 min read

What you'll learn

  • How call site determines this
  • Default, implicit, explicit, and new binding
  • Why arrow functions ignore this rules
  • Using bind, call, and apply
  • Common bugs and how to avoid them

Prerequisites

  • Familiarity with JavaScript basics

Few features of JavaScript cause as much confusion as the this keyword. The good news is that the rules are simpler than they appear. Once you learn the four binding rules, you can predict what this will be in any situation.

What this Actually Is

this is a special identifier that JavaScript sets implicitly when a function is called. It is not determined by where the function is defined — only by how it is called. This is the single most important rule.

function show() {
  console.log(this);
}

show();             // global object (or undefined in strict mode)
const obj = { show };
obj.show();         // obj

Same function body, two different values for this. The call site is what matters.

The Four Binding Rules

There are four ways this can be bound, ordered by precedence. Once you learn these, every situation becomes predictable.

First is the default binding. When a function is called with no context, this is the global object in sloppy mode and undefined in strict mode.

Second is implicit binding. When a function is called as a method on an object, this is that object.

Third is explicit binding. You can force this with call, apply, or bind.

Fourth is new binding. When a function is called with new, this is the newly created object.

Implicit Binding and Losing It

The trickiest situations come from losing implicit binding. If you assign a method to a variable and then call it, the context is gone.

const user = {
  name: 'Ada',
  greet() { console.log(`Hi, ${this.name}`); },
};

user.greet();             // Hi, Ada
const fn = user.greet;
fn();                     // Hi, undefined

The same thing happens when you pass a method as a callback. The receiver no longer calls it as a method, so this is lost.

Explicit Binding

To force a specific this, use call, apply, or bind. They are nearly identical except for how arguments are passed.

function greet(greeting) {
  console.log(`${greeting}, ${this.name}`);
}

const user = { name: 'Ada' };
greet.call(user, 'Hi');   // Hi, Ada
greet.apply(user, ['Hi']); // Hi, Ada

const bound = greet.bind(user);
bound('Hi');              // Hi, Ada

bind returns a new function with this permanently fixed, which is great for callbacks that would otherwise lose context.

Arrow Functions Are Different

Arrow functions do not have their own this. Instead they inherit it from the surrounding lexical scope, just like any other variable. This makes them ideal for callbacks inside methods.

class Timer {
  constructor() {
    this.seconds = 0;
    setInterval(() => {
      this.seconds += 1;
    }, 1000);
  }
}

If we used a regular function inside setInterval, this would be the timer system, not the Timer instance. The arrow function captures this from the constructor.

You cannot rebind an arrow function’s this with call or bind. The lexical binding wins, always.

new Binding

When you call a function with new, JavaScript creates a fresh object, sets it as this, runs the body, and returns it.

function User(name) {
  this.name = name;
}

const u = new User('Ada');
console.log(u.name); // Ada

This is the basis of pre-class JavaScript constructors. Modern class syntax compiles down to roughly the same behavior.

Common Pitfalls

Two patterns cause most bugs around this. First, passing a method as an event handler without binding it loses context — always wrap with an arrow function or bind.

Second, defining methods as arrow functions on classes is sometimes desired (for auto-binding) but means each instance gets its own copy of the function. For most cases, regular methods plus bind in the constructor is fine.

Wrapping Up

The this keyword feels mysterious until you internalize that it is set at the call site, not at definition. Memorize the four binding rules, prefer arrow functions for inner callbacks, and you will rarely be surprised again.