article

Why Does this Hate Me? Demystifying JavaScripts this

3 min read

Ah yes, the infamous this keyword in JavaScript — responsible for more broken code and debugging tears than any other. In this article, we’ll laugh through the pain and explain what this actually means in different contexts.

Rule 1: this in the Global Scope

At the top level, this is the global object — window in browsers, globalThis in Node.

console.log(this === globalThis); // true in Node

Harmless here. The trouble starts when this travels.

Rule 2: this Inside a Regular Function

In a regular function, this depends on how the function is called, not where it is defined.

function greet() {
  console.log(this.name);
}

const user = { name: "Alice", greet };
user.greet(); // "Alice" — called as a method, this = user

const fn = user.greet;
fn();         // undefined in strict mode — called standalone

The moment you detach the function from the object, this evaporates.

Rule 3: Arrow Functions Don’t Have Their Own this

Arrow functions capture this from the enclosing lexical scope at definition time — exactly what you want inside callbacks.

class Timer {
  constructor() { this.seconds = 0; }
  start() {
    setInterval(() => {
      this.seconds++; // `this` is the Timer instance
    }, 1000);
  }
}

A regular function inside setInterval would lose this entirely.

Rule 4: Explicit Binding

You can hard-wire this using .call(), .apply(), or .bind().

function introduce(greeting) {
  console.log(`${greeting}, I'm ${this.name}`);
}

const person = { name: "Bob" };

introduce.call(person, "Hey");       // Hey, I'm Bob
introduce.apply(person, ["Howdy"]);  // Howdy, I'm Bob

const boundIntro = introduce.bind(person);
boundIntro("Hi");                    // Hi, I'm Bob

.bind() returns a new function with this permanently set — great for event handlers.

Rule 5: this in Classes

Inside a class, this refers to the instance — as long as the method is called on it.

class Dog {
  constructor(name) { this.name = name; }
  bark() { console.log(`${this.name} says: woof`); }
}

const rex = new Dog("Rex");
rex.bark(); // Rex says: woof

const bark = rex.bark;
bark();     // undefined — `this` is lost

Fix it with a class field arrow function:

class Dog {
  bark = () => console.log(`${this.name} says: woof`);
}

Quick Reference

How it’s calledthis value
Global / standaloneglobalThis (or undefined in strict)
As an object methodThe object before the dot
Arrow functionLexical this from outer scope
.call / .apply / .bindFirst argument
new constructorThe new instance

Takeaway

this is not broken — it is just dynamic. Once you internalize the five rules above, its behavior becomes predictable. When in doubt, use an arrow function or .bind() and move on.