Skip to content
JavaScript

The 'this' Keyword in JavaScript, Finally Explained

By The EbookWale Team · Updated June 16, 2026 · 5 min read

In JavaScript, 'this' depends on how a function is called, not where it is defined. Here are the four binding rules, why arrow functions are different, and how to fix the classic 'this is undefined' bug.

In JavaScript, this refers to whatever object is calling the function — it is decided at call time, not where the function is written. The same function can have four different this values depending on how you invoke it, which is exactly why this confuses people coming from other languages.

Get the four rules straight and the confusion disappears. Better still, you will be able to explain the most common bug in beginner JavaScript — “why is this suddenly undefined?” — which is a frequent interview question.

The mental shift

In many languages, this (or self) reliably points to the current instance. In JavaScript, a function does not own a permanent this. Instead, this is bound fresh every time the function is called, based on the call site. So you cannot answer “what is this?” by looking at the function — you have to look at how it is called.

There are exactly four rules. Check them in this order.

obj.method() fn() new Fn() fn.call(x) () => { } obj (before the dot) undefined the new object x inherited (lexical)
this depends on HOW a function is called — except arrows.

Rule 1 — Method call: the object before the dot

When you call a function as a method, this is the object to the left of the dot.

const user = {
  name: "Ana",
  greet() {
    return `Hi, I'm ${this.name}`;
  },
};
user.greet(); // "Hi, I'm Ana"  → this === user

This is the case people assume always holds. It does not.

Rule 2 — Plain call: undefined (or the global object)

Call the same function without an object in front, and there is nothing to the left of the dot:

const greet = user.greet; // detached from `user`
greet(); // this.name → TypeError, because this is undefined (strict mode)

In strict mode (and inside ES modules, which are always strict), a plain call sets this to undefined. In sloppy mode it falls back to the global object (window). Either way, this.name is not what you wanted.

⚠️ GOTCHA — The instant you detach a method — assign it to a variable, pass it as a callback, destructure it — it is called plainly and loses its object. This is the cause of 90% of "this is undefined" bugs.

Rule 3 — new call: a brand-new object

When you call a function with new (a constructor), this is a fresh empty object that the function fills in and returns automatically.

function User(name) {
  this.name = name; // this === the new object
}
const u = new User("Ben");
u.name; // "Ben"

Classes work the same way underneath — class is sugar over this constructor mechanism.

Rule 4 — Explicit binding: call, apply, bind

You can force this to be whatever you want.

function greet() {
  return `Hi, I'm ${this.name}`;
}
const ana = { name: "Ana" };

greet.call(ana);          // "Hi, I'm Ana"  — invoke now, args listed
greet.apply(ana);         // same, but args as an array
const bound = greet.bind(ana); // returns a new function, this locked to ana
bound();                  // "Hi, I'm Ana"

call and apply invoke immediately; bind returns a new function with this permanently fixed. bind is the classic manual fix for lost-this callbacks.

The exception that fixes everything: arrow functions

Arrow functions do not follow the four rules at all. They have no this of their own — they capture this from the surrounding scope where they were written, once, and it never changes. This is lexical this, and it behaves like a closure over this.

That is why arrows are the modern fix for the most common this bug:

class Timer {
  constructor() {
    this.seconds = 0;
  }
  start() {
    // Arrow: `this` stays the Timer instance, because it's captured
    // from start()'s scope — not re-bound by setInterval.
    setInterval(() => {
      this.seconds++;
      console.log(this.seconds);
    }, 1000);
  }
}

If you used a regular function () { this.seconds++ } there, setInterval would call it plainly and this would be wrong. The arrow sidesteps the whole problem.

🔑 REMEMBER — Regular functions get this from how they are called. Arrow functions get this from where they are written. That one distinction resolves almost every this question.

A decision table

How the function is calledWhat this is
obj.method()obj (the thing before the dot)
fn() (plain)undefined in strict mode / modules
new Fn()the new object being constructed
fn.call(x) / fn.apply(x) / fn.bind(x)x, the value you provided
arrow functioninherited from the enclosing scope (lexical)

Run through it top to bottom at any call site and you will always know this.

Common mistakes

  • Passing a method as a callbacksetTimeout(obj.method, 100) loses this. Use setTimeout(() => obj.method(), 100) or obj.method.bind(obj).
  • Using an arrow function as an object method when you actually want this to be the object — an arrow won’t bind to it. Use a normal method for that.
  • Expecting this in a plain standalone function to be useful — it usually isn’t. If you need context, pass it as an argument or use a closure.

Where this fits

this, alongside closures and the event loop, is the Phase 5 “tricky internals” set in our JavaScript roadmap — small in surface area, huge in interview frequency.

These rules are drawn out with diagrams and worked call-site walkthroughs in JavaScript in Three Months, and JavaScript in One Month introduces them gently for newer developers. For binding edge cases, decorators, and how this behaves in class fields and framework internals, JavaScript for Staff Engineers goes deeper.

Stop reading this as “the current object.” Read it as “look at the call.” Everything else follows.

Frequently asked questions

What does 'this' refer to in JavaScript?

It depends on how the function is called. As a method (obj.fn()), this is the object before the dot. Called plainly (fn()), this is undefined in strict mode or the global object otherwise. With new, this is the brand-new object. With call/apply/bind, this is whatever you pass. Arrow functions ignore all of this and inherit this from where they were defined.

Why is 'this' undefined inside my method?

Almost always because the method was detached from its object — passed as a callback (setTimeout, addEventListener) or destructured — so it is called plainly rather than as obj.method(). Fix it by using an arrow function, or by binding the method with .bind(this).

How are arrow functions different with 'this'?

Arrow functions do not get their own this. They capture this lexically from the surrounding scope at the moment they are defined, and it never changes. That is exactly why arrow functions are the go-to fix for lost-this callbacks.

What is the difference between call, apply, and bind?

All three set this explicitly. call invokes the function immediately with arguments listed individually; apply does the same but takes arguments as an array; bind does not call the function — it returns a new function with this permanently fixed.