The JavaScript this Keyword: What It Really Points To

this is the most misunderstood word in JavaScript. Learn the simple rule behind it: this is decided by HOW a function is called, not where it's written — plus arrow functions, bind, and the classic gotchas.

Published August 6, 202610 min readBy ACY Partner Indonesia
The JavaScript this keyword — what it points to depends on the call
300 × 250Ad Space AvailablePlace your ad here

Few things in JavaScript cause as much confusion as the keyword this. People coming from other languages expect it to mean “the current object,” the way it does in Java or C#. In JavaScript it’s looser than that — and far more surprising. The same function can have this pointing to one thing on Monday and a completely different thing on Tuesday, depending on nothing more than how you called it.

Here’s the good news up front: once the rule clicks, this stops being magic. The whole topic comes down to a single idea — this is decided at call time, by how the function is invoked, not by where the function was written. Get that into your bones and the rest is detail. Let’s build it up carefully.

You’ll be more comfortable here if you’ve already met functions and objects, since this lives at the intersection of the two.

What is this?

this is a special keyword that exists automatically inside every function. It refers to some object — but which object isn’t fixed. JavaScript fills in this fresh each time the function runs, based on the way the call was made.

That’s the part that trips everyone up. In most languages, a method belongs to its class and this always points to the instance. In JavaScript a function is just a value; it doesn’t “belong” to anything permanently. So the question is never “what does this mean inside this function?” The real question is always: “how was this function called?” Answer that, and you know this.

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

That same show function could print a window object, an undefined, or a user object — entirely depending on the call site. Below we’ll go through every way a function can be called, and what this becomes in each case.

The four call patterns

There are four common ways to invoke a function, and each one sets this differently. Learn these four and you’ve covered nearly everything you’ll meet in real code.

1. Method call: this is the object before the dot

When you call a function as a property of an object — object.method()this becomes the object that’s sitting to the left of the dot. This is the most intuitive case and the one you’ll use most.

const user = {
  name: "John Doe",
  greet() {
    console.log("Hi, I'm " + this.name);
  },
};

user.greet();   // "Hi, I'm John Doe"

Inside greet, this is user, because the call was user.greet()user is the thing before the dot. So this.name reads user.name. Simple and predictable.

The key insight: it’s the call that matters, not where the function was defined. Watch what happens if we pull the method off the object:

const greetAlone = user.greet;
greetAlone();   // 💥 this.name is now undefined (or an error)

Same function, but now there’s no object before the dot, so this is no longer user. The function didn’t change — the way we called it did. This single example is the heart of the whole topic.

2. Plain function call: this is undefined (or the global object)

When you call a function on its own — no object, no dot — this depends on whether you’re in strict mode:

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

standalone();
// strict mode (modules, classes): undefined
// non-strict (old scripts):       the global object (window in a browser)

In modern JavaScript — ES modules, class bodies, anything with "use strict" — a plain function call gives this === undefined. That’s actually helpful, because it turns silent bugs into loud errors instead of quietly writing to the global object.

The lost this problem

This is the single most common this bug. You pull a method out of its object and call it later — through a variable, a callback, or a setTimeout — and suddenly this is gone:

const user = { name: "John Doe", greet() { return this.name; } };

setTimeout(user.greet, 1000);   // this is NOT user here!

setTimeout doesn’t call it as user.greet() — it stores the function and later calls it plain, with no object before the dot. So this is no longer user. The fixes (arrow functions, bind) come up below — but first, recognize the pattern: any time a method gets passed around and called elsewhere, its this is at risk.

3. Constructor call: this is the brand-new object

When you call a function with the new keyword, JavaScript creates a fresh empty object and points this at it. The function fills in that object, and (unless you return something else) new hands it back automatically.

function User(name) {
  this.name = name;
  this.role = "member";
}

const u = new User("Jane Doe");
console.log(u.name);   // "Jane Doe"
console.log(u.role);   // "member"

Here this is the new object being built. The same thing happens inside a class constructor — classes are mostly nicer syntax over exactly this mechanism. If you’ve used new Date() or new Map(), you’ve already relied on this pattern without thinking about it.

4. Explicit call: you choose this with call, apply, bind

Sometimes you want to decide this yourself, regardless of how the function would normally be called. JavaScript gives you three methods on every function for exactly that: call, apply, and bind.

function greet(greeting) {
  console.log(greeting + ", " + this.name);
}

const person = { name: "John Doe" };

greet.call(person, "Hello");    // "Hello, John Doe"
greet.apply(person, ["Hi"]);    // "Hi, John Doe"

const bound = greet.bind(person);
bound("Hey");                   // "Hey, John Doe"

call and apply invoke the function right now with a this you specify — the only difference is call takes arguments one by one, while apply takes them as an array. bind is different: it doesn’t call the function, it returns a new function with this permanently locked to whatever you passed. That returned function will keep that this no matter how it’s later called — which is exactly what makes bind the classic fix for the “lost this” problem.

Remembering call vs apply

call and apply do the same job; only how you pass arguments differs. A tiny memory hook: apply takes an array. call spreads them out: fn.call(thisArg, a, b, c). apply bundles them: fn.apply(thisArg, [a, b, c]). With the spread operator (...) you rarely need apply anymore — fn.call(thisArg, ...args) covers it.

Arrow functions: this is inherited, not reset

Arrow functions break the rules above — on purpose, and helpfully. An arrow function has no this of its own. It doesn’t care how it was called. Instead, it borrows this from the surrounding code where it was written — its lexical scope. This is the one place where “where it’s written” actually matters.

That sounds abstract until you see the problem it solves. This is one of the most common real bugs:

const timer = {
  seconds: 0,
  start() {
    setInterval(function () {
      this.seconds++;            // 💥 this is NOT timer here
      console.log(this.seconds); // NaN, over and over
    }, 1000);
  },
};

The callback inside setInterval is a plain function call, so its this is undefined (or the global object), not timer. The increment silently fails. Now swap in an arrow function:

const timer = {
  seconds: 0,
  start() {
    setInterval(() => {
      this.seconds++;            // ✅ this is timer
      console.log(this.seconds); // 1, 2, 3, ...
    }, 1000);
  },
};

The arrow function has no this of its own, so this inside it is whatever this was in start — and start was called as timer.start(), so its this is timer. The arrow simply inherits it. That’s why arrows are the go-to choice for callbacks inside methods.

Don't use an arrow function as an object method

Because arrows inherit this instead of getting their own, they’re the wrong choice for a top-level method on an object:

const user = {
  name: "John Doe",
  greet: () => {
    console.log(this.name);   // undefined — NOT user
  },
};
user.greet();

Here the arrow inherits this from the surrounding scope (the module or global), not from user — so this.name is wrong. The rule of thumb: use a regular function (or method shorthand) for object methods so this can bind to the object, and use an arrow function for callbacks inside those methods so they inherit the right this.

A complete mental model

Put it all together and you can read this in any code by asking one question at the call site — how was this function called? Walk down the list in order:

// 1. new Fn()        → this is the brand-new object
// 2. obj.fn()        → this is obj (the thing before the dot)
// 3. fn.call(x) etc. → this is x (whatever you passed)
// 4. fn()            → this is undefined (strict) / global (sloppy)
// 5. arrow function  → ignore all of the above; this comes from the
//                      surrounding scope where the arrow was written

Notice that arrow functions sit outside the list — they opt out of the whole mechanism and borrow from their surroundings. For every other function, the answer is at the call site, never the definition. When this surprises you, don’t re-read the function body. Go find where it was called and run through these cases. The answer is always there.

When to use what

A few practical habits that keep this out of your way:

  • Object methods → regular functions or method shorthand (greet() { ... }). They need their own this so it can bind to the object.
  • Callbacks inside a method → arrow functions. They inherit the method’s this, which is almost always what you want.
  • Passing a method as a callback → bind it (this.handle = this.handle.bind(this)) or wrap it in an arrow (() => this.handle()), so its this survives the trip.
  • Modern classes → either define handlers as arrow-function class fields, or bind in the constructor. Both keep this pointing at the instance.

If you’ve worked with scope and closures, notice the contrast: ordinary variables are resolved lexically (by where the code sits), but this for regular functions is resolved dynamically (by how the call happens). Arrow functions are the bridge — they make this behave lexically, like a normal variable. That single difference explains most of the confusion people have with this.

Wrapping up

The keyword this only feels chaotic until you internalize the one rule:

  • this is set by how a function is called, not where it’s defined. When in doubt, look at the call site.
  • Method call (obj.fn()) → this is the object before the dot.
  • Plain call (fn()) → this is undefined in strict mode, the global object otherwise.
  • Constructor call (new Fn()) → this is the freshly created object.
  • Explicit call (call, apply, bind) → this is whatever you pass; bind locks it permanently into a new function.
  • Arrow functions have no this of their own — they inherit it from the surrounding scope. Perfect for callbacks inside methods; wrong for object methods themselves.

Master those cases and the most “magical” word in JavaScript becomes one of the most predictable. Next time this is undefined, you’ll know exactly where to look — and you’ll usually fix it with an arrow function or a bind in seconds.

Tags:javascriptfrontendthisbindingintermediate
728 × 90Ad Space AvailablePlace your ad here

Related Articles

See All Articles

You Might Also Like

Frontend best practices overview cover
Frontend / Fundamentals

Frontend Best Practices: An Overview

A friendly map of what good frontend really means — semantic HTML, clean CSS, unobtrusive JavaScript, responsive layouts, performance, and progressive enhancement, with pointers to go deeper.

Sep 20, 20268 min read
A Frontend Developer Roadmap cover with the path HTML to CSS to JavaScript
Frontend / Fundamentals

A Frontend Developer Roadmap: What to Learn, and in What Order

A calm, step-by-step learning path for beginners: HTML, CSS, JavaScript, developer tools, responsive and accessible design, a framework, TypeScript, and deployment, with the reasoning behind each step.

Sep 20, 202611 min read
Title card reading Styling and Interactivity over a dark blue ACY Partner background
Frontend / Fundamentals

How Styling and Interactivity Work

A beginner-friendly look at how CSS turns plain HTML into a designed layout, and how JavaScript adds behavior — the structure, style, behavior mental model for building web pages.

Sep 20, 20269 min read
Three front-end layers combining on one web page
Frontend / Fundamentals

How HTML, CSS, and JavaScript Work Together

A hands-on walkthrough for beginners: build one small button, give it structure with HTML, looks with CSS, and behavior with JavaScript, and watch the three layers cooperate on a real page.

Sep 20, 20268 min read
The Frontend Developer Toolkit cover with editor, browser, and CLI motifs
Frontend / Fundamentals

The Frontend Developer Toolkit

A friendly tour of the everyday tools a frontend developer relies on — code editor, browser and DevTools, terminal, version control, package managers, and a dev server — and what each one is actually for.

Sep 20, 202610 min read
Cover illustration for What Frontend Developers Do
Frontend / Fundamentals

What Frontend Developers Do: A Beginner's Guide to the Role

A clear, beginner-friendly look at what frontend developers actually do every day: turning designs into working interfaces, building reusable UI, and caring about responsiveness, accessibility, and speed.

Sep 20, 202610 min read