Technology Apr 30, 2026 · 7 min read

"This" Keyword

Hello readers 👋, welcome to the 18th blog in this JavaScript series! Today we are going to tackle a topic that confuses almost every JavaScript developer at some point: the this keyword. It can feel unpredictable if you don't understand what sets its value, but once you realize that this is simply...

DE
DEV Community
by SATYA SOOTAR
"This" Keyword

Hello readers 👋, welcome to the 18th blog in this JavaScript series!

Today we are going to tackle a topic that confuses almost every JavaScript developer at some point: the this keyword. It can feel unpredictable if you don't understand what sets its value, but once you realize that this is simply determined by how a function is called, not where it's defined, everything starts to click.

We will keep things simple, practical, and visual. No deep internal engine details, just a clear mental model you can rely on every time you see this in your code.

Let's begin.

What does this represent?

Think of this as a special variable that points to the caller of a function, or more accurately, the execution context in which a function runs. In most cases, you can figure out the value of this by looking at the dot before the function name. If there is an object before the dot, this refers to that object. If there is no dot, this refers to the global object (or is undefined in strict mode).

This one rule covers a huge number of situations. Let's see how it plays out in different contexts.

this in the global context

When you are not inside any function or object, this points to the global object. In a browser, that's the window object. In Node.js, it's global. You can test this easily:

console.log(this); // In browser: window object

If you open your browser console and type this, you'll see the window object printed. In a Node REPL, you'll see global. That's the simplest case.

this inside a regular function

Here is where people often trip up. In a standalone function call (without an object), the value of this depends on whether strict mode is enabled or not.

In non-strict mode, this inside a function called like myFunc() will be the global object:

function showThis() {
  console.log(this);
}
showThis(); // window (in browser, non-strict)

In strict mode (with "use strict"), this is undefined. This is actually better because it prevents accidentally poking the global object.

"use strict";
function showThis() {
  console.log(this);
}
showThis(); // undefined

The takeaway: a plain function call sets this to the global object or undefined in strict mode. But in both cases, it's not something we typically want.

this inside an object method

When a function is called as a method of an object (with object.method()), this points to the object that the method belongs to. This is the most intuitive and common use case.

const person = {
  name: "Satya",
  greet: function() {
    console.log("Hello, I'm " + this.name);
  }
};

person.greet(); // Hello, I'm Satya

Here, person is the caller. The dot before greet() tells us that this inside greet is the person object. So this.name gives "Satya". This works beautifully for sharing data among methods of the same object.

We can also have multiple objects sharing the same method definition. The value of this will change based on which object called the method:

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

const user1 = { name: "Amit", speak: sayName };
const user2 = { name: "Priya", speak: sayName };

user1.speak(); // Amit
user2.speak(); // Priya

Notice that sayName is the same function, but because we call it via user1.speak(), this becomes user1. When called via user2.speak(), this becomes user2. The function itself doesn't own a fixed this. The call site decides.

How the calling context changes this

The magic of this lies in its dynamic nature. But this also means you can lose the intended binding if you call a method without its object. This happens often with callbacks or when you assign a method to a variable.

Let's see a classic pitfall:

const counter = {
  count: 0,
  increment: function() {
    this.count++;
    console.log(this.count);
  }
};

const inc = counter.increment;
inc(); // NaN or error

What happened? We assigned counter.increment to inc and then called inc() without any object before the dot. That makes this become the global object (or undefined in strict mode). this.count is then undefined, and incrementing it gives NaN. The original counter.count remains untouched.

To fix this, we need to explicitly bind this to a specific object. JavaScript gives us three powerful tools for that: call, apply, and bind.

call and apply

Both call and apply invoke the function immediately with a specified this value. The difference is how you pass arguments.

function introduce(city, country) {
  console.log("I am " + this.name + " from " + city + ", " + country);
}

const user = { name: "Satya" };

introduce.call(user, "Bhubaneswar", "India");
introduce.apply(user, ["Bhubaneswar", "India"]);

Both lines set this to user. call takes arguments one by one, apply takes an array of arguments. This is useful when you need to borrow methods or reuse functions with different contexts.

bind

bind is different. It doesn't call the function immediately. Instead, it returns a new function where this is permanently locked to the given object.

const counter = {
  count: 0,
  increment: function() {
    this.count++;
    console.log(this.count);
  }
};

const boundInc = counter.increment.bind(counter);
boundInc(); // 1
boundInc(); // 2

Even though we call boundInc() without an object, this inside it is always counter. This is incredibly handy for passing methods as callbacks without losing the intended context.

Visualizing the caller relationship

A simple mental picture can help. Imagine every function being a piece of code waiting for an instruction. When you call it through an object, it's like the object hands over its identity key to the function, saying, "I am the one calling you, use me as this."

caller.method()
   ^
   |
 this = caller

If there is no caller object, JavaScript falls back to the global environment (or undefined in strict mode). Explicit binding with call, apply, or bind is like personally handing over a specific identity key, no matter how the function is called later.

This is why losing context happens: you might pass a function without its object, and the default fallback kicks in.

Arrow functions and this (a quick note)

You will likely encounter arrow functions, and they handle this differently. Arrow functions don't have their own this. They inherit this from the surrounding scope at the time they are defined. This is called lexical scoping.

For example:

const person = {
  name: "Satya",
  greet: function() {
    const inner = () => {
      console.log(this.name);
    };
    inner();
  }
};

person.greet(); // Satya

Even though inner() is called like a regular function, it uses the this from greet because that's where it was defined. This behavior makes arrow functions very convenient as callbacks inside methods.

But keep in mind: if you define a method itself as an arrow function, it will capture the global this or the this of the enclosing scope, not the object. So it's best to use regular functions for object methods unless you have a specific reason.

Summary of contexts

Let's put everything together in a quick reference:

Context Value of this
Global scope (outside any function) Global object (window, global)
Regular function call (non-strict) Global object
Regular function call (strict mode) undefined
Method call (obj.method()) The object before the dot (obj)
call / apply Explicitly provided value
bind A new function with permanently bound this
Arrow function Lexical this from surrounding scope
Constructor call (new Func()) The newly created instance

We haven't covered constructors in this post, but they also set this to the fresh instance. That's a topic for another day.

Conclusion

The this keyword is not magical. It's a dynamic reference that depends on how a function is invoked. Once you train yourself to look at the call site, this becomes predictable and powerful.

To recap the key points:

  • this represents the calling object when a function is called as a method.
  • In a plain function call (non-strict), this is the global object; in strict mode, it's undefined.
  • You can explicitly set this using call, apply, and bind.
  • Arrow functions don't have their own this; they capture it from the enclosing scope.
  • The most common pitfall is losing context when passing a method as a callback, which bind easily fixes.

I hope this clears up the mystery around this and gives you confidence to use it intentionally. With this foundation, you are well equipped to handle more complex patterns like constructor functions, classes, and event handling.

Hope you found this helpful! If you spot any mistakes or have suggestions, let me know. You can find me on LinkedIn and X, where I post more about web development.

DE
Source

This article was originally published by DEV Community and written by SATYA SOOTAR.

Read original article on DEV Community
Back to Discover

Reading List