Skip to main content

Command Palette

Search for a command to run...

JavaScript's this Keyword: Who's Calling?

Updated
•5 min read•View as Markdown

The One Rule You Need to Know

Before anything else, burn this into your brain:

this refers to whoever called the function — not where the function was written.

That's it. Everything else is just variations of this single idea. Let's walk through each one.

What Does this Represent?

In JavaScript, this is a special keyword that refers to an object. But which object? The one that is currently calling the function.

Think of it like a name tag that changes depending on who's wearing it. The same function can have a different this value depending on how and where it gets called.

function greet() {
  console.log("Hi, I am", this.name);
}

This function doesn't know what this.name is yet. It depends entirely on who calls greet()

this in the Global Context

When you're at the top level of your script — outside any function or object — this refers to the global object.

  • In a browser, the global object is window

  • In Node.js, the global object is global

console.log(this); // In browser → Window { ... }

this.appName = "MyApp";
console.log(window.appName); // "MyApp" — same thing

In strict mode, this is undefined

When you add "use strict" at the top of a file or function, JavaScript stops pointing this at the global object inside regular functions. Instead, it becomes undefined — which is actually safer behaviour.

"use strict";

function whoAmI() {
  console.log(this); // undefined — not window!
}

whoAmI();

This prevents accidental pollution of the global scope, which is a common source of bugs.


this Inside Objects

When a function is called as a method of an object, this refers to that object. This is the most intuitive use of this.

const user = {
  name: "Priya",
  greet() {
    console.log("Hello, I'm", this.name); // ✅ "Hello, I'm Priya"
  }
};

user.greet();
// ↑ user is the caller, so this = user

The key here is the dot notation. When you write user.greet(), the object to the left of the dot (user) becomes this inside greet.

Nested objects follow the same rule

const company = {
  name: "TechCorp",
  ceo: {
    name: "Arjun",
    introduce() {
      console.log(`I'm ${this.name}, CEO of...`);
      // this = ceo (the direct caller), not company!
    }
  }
};

company.ceo.introduce(); // "I'm Arjun, CEO of..."

this is always the immediate object before the dot — not the outermost one.

this Inside Functions

This is where most developers get confused. The behaviour depends on how the function is called.

Regular function call — this is global (or undefined)

When you call a function on its own — not as a method — this defaults to the global object. In strict mode, it's undefined.

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

showThis(); // Window (browser) or undefined (strict mode)

The classic bug: losing this inside a method

Here's something that trips up almost every JavaScript developer at least once:

const timer = {
  label: "Countdown",
  start() {
    setTimeout(function() {
      console.log(this.label); // ❌ undefined!
    }, 1000);
  }
};

timer.start();

Why does this break? Because setTimeout calls the callback function on its own — not as timer.start. So this inside the callback is window, not timer. window.label doesn't exist, so you get undefined.

Arrow functions fix this — they don't have their own this

Arrow functions are different. They don't get their own this. Instead, they inherit this from the surrounding code where they were written.

const timer = {
  label: "Countdown",
  start() {
    setTimeout(() => {
      console.log(this.label); // ✅ "Countdown"
    }, 1000);
  }
};

timer.start();

Now this inside the arrow function is the same this as inside start() — which is timer. Arrow functions are the modern, clean solution to callback this problems.

How the Calling Context Changes this

JavaScript also gives you three methods to explicitly set what this should be: .call(), .apply(), and .bind().

.call() — call immediately, pass this as first argument

function introduce(role) {
  console.log(`I'm \({this.name}, the \){role}`);
}

const person = { name: "Kavya" };

introduce.call(person, "developer");
// "I'm Kavya, the developer"

.apply() — same as .call(), but arguments go in an array

introduce.apply(person, ["designer"]);
// "I'm Kavya, the designer"

.bind() — returns a new function with this permanently set

const boundIntroduce = introduce.bind(person);
boundIntroduce("tester"); // "I'm Kavya, the tester"
boundIntroduce("manager"); // "I'm Kavya, the manager" — this is always person

.bind() is especially useful when passing methods as callbacks, where you want to lock in a specific this.

const button = {
  label: "Submit",
  handleClick() {
    console.log(`${this.label} was clicked`);
  }
};

// Without bind — this is lost
document.addEventListener("click", button.handleClick); // ❌ undefined

// With bind — this is locked
document.addEventListener("click", button.handleClick.bind(button)); // ✅ "Submit was clicked"

Quick Reference: this in Every Context

Where this is used What this equals
Global scope (browser) window
Global scope (Node.js) global
Global / standalone function window (or undefined in strict mode)
Object method (obj.fn()) The object (obj)
Arrow function Inherited from surrounding scope
Event listener callback The DOM element that fired the event
.call(ctx) / .apply(ctx) Whatever you passed as ctx
.bind(ctx) (new function) Whatever you passed as ctx

The Mental Model to Remember

Instead of thinking "what does this mean here?", always ask:

"Who is calling this function right now?"

  • Is it an object? → this is that object

  • Is it called alone? → this is global (or undefined)

  • Is it an arrow function? → look at where the function was written, not called

  • Did someone use .bind() / .call() / .apply()? → this is whatever they passed in

More from this blog