What Is the this Keyword in JavaScript?

Introduction

The this keyword is a somewhat confusing concept in JavaScript because it behaves differently depending on the context, for example, when used in an object, a regular function, or an arrow function. It also behaves differently in strict mode versus non-strict mode. This inconsistency is what makes it so confusing. I’ve read many articles on this topic, but I’ve come to realize that although they explain the concept in depth, they don’t necessarily make it easier to understand. Today, we’ll aim to gain a deeper understanding of this concept.

What is the `this` Keyword in JavaScript?

this in the Global Scope

Generally, the this keyword refers to the global object. In browsers, that global object is window. So, if we use this outside of a function or object, meaning in the global scope, it will return the global window object (in non-strict mode). Check the example below:

JavaScript

copy

console.log(this);
// output: Window {window: Window, self: Window, document: document, name: '', location: Location, …}

Check this output and you will understand. Did you see that this returned the global window object?

However, this returns different outputs in different runtime environments. For example, in a browser, it returns window as shown above, but in Node.js, it behaves differently. In Node.js, if you use this in the global scope (like directly in a .js file), it will return an empty object instead of the global object. So, long story short, its output varies depending on the environment and the context in which it is used.

But you may have this question in your mind: Why does it return the window global object if we use it outside of any function or object?

The answer is straightforward. In the browser, the global object is window. If you run code outside any object, this will point to the window. Even in strict mode, at the top level, this still refers to the global object. Strict mode only changes the behavior of this inside functions. Because we're at the top level of a script, JavaScript doesn't know any other object context, so it uses the global object (window in browsers).

So I hope the doubts are cleared. Along with that, we have learned the fundamentals of the this keyword.

Let’s cover how it behaves in the local scope, like when using it inside a function or an object. We will see both of them independently, as this behaves differently in each case.

this in the Local Scope

As we know, it behaves differently in local and global scopes. We’ve already talked about the global scope, so now we’ll explore the local scope in more depth.

this in functions

I’ve used functions instead of function because I’ll cover both normal and arrow functions under this heading 🙂. I had no other choice 😀.

Let’s start with a simple function:

Simple function

In a regular function, the this keyword behaves the same as it does in the global scope by returning the window’s global object, but only in non-strict mode. In strict mode, it returns undefined. We’ll go deeper into this later. For now, let’s get started with a basic example:

JavaScript

copy

function test() {
  console.log(this); // output: Window {window: Window, self: Window, document: document, name: '', location: Location, …}
}
test();

Did you notice how the output inside the function is the same as it is in the global scope in non-strict mode? Let’s now see the same example in strict mode.

JavaScript

copy

'use strict'
function test() {
  console.log(this); // undefined
}
test();

Have you seen that we executed the same code in strict mode (strict mode is enabled by using use strict at the top of the file)? There are huge differences in output, but why does this happen?

To understand this, we have to go a little deeper into strict and non-strict mode. By default, JavaScript runs in non-strict mode, where it allows some sloppy behaviors, like in our this case. But when we enable strict mode, JavaScript doesn’t ignore anything; it strictly follows the rules. Strict mode was introduced in ES5, and it prevents silent errors and makes JavaScript behave more predictably.

Now let's get back to the point where we left off.

When we run this inside a function in non-strict mode, it returns the value of the global object (like window in browsers). This happens because, in the global scope, this points to the global object, so inside the function, without strict mode, this defaults to the global object too JavaScript just lets it slide as a small issue.

But as we enable strict mode, JavaScript becomes stricter, and it says, Hi, this, you are not allowed to adopt the behaviour of the global scope. Now you are in a function scope, so you will only be allowed to return the value that functions provide you; otherwise, you will return “undefined,” which means you don’t have any value.

So, for this reason, in the strict mode example above, this returned undefined. I hope this clears up your doubt.

Now let’s move on to arrow functions.

Arrow function

In an arrow function, this behaves slightly differently compared to a normal function. Arrow function doesn’t have its own this, so it depends on the surrounding (lexical) scope, the place where it is defined, not called. So in non-strict mode, it returns the same as a normal function does.

JavaScript

copy

const test = () => {
  console.log(this); // output: Window {window: Window, self: Window, document: document, name: '', location: Location, …}
 }
test();

Have you noticed the output looks the same as with a normal function? But in strict mode, the behavior can be surprising. Check out the example below:

JavaScript

copy

'use strict'
const test = () => {
  console.log(this); // output: Window {window: Window, self: Window, document: document, name: '', location: Location, …}
}
test();

Did you notice it still returns the window global object even in strict mode? But why? You might have this doubt in your mind. In a simple function, in strict mode, it returns undefined due to strict mode’s stricter behavior. But in an arrow function, why is it different? Weird!

Of course, it is weird. However, only one thing makes it unsmooth: arrow functions don’t have their own this, and they inherit it from the surrounding scope. This means they behave the same in both strict and non-strict modes. You can take it as a downside or an upside.

There is one more thing I want to cover before winding up this section, which represents the further difference explicitly between normal and arrow functions.

JavaScript

copy

'use strict'
function testParent() {
const test = () => {
  console.log(this); // output: undefined
}
test();
}
testParent();

Did you notice the output is undefined? It’s confusing because, as we knew earlier, even in strict mode, an arrow function won’t return undefined for this since it doesn’t have its own this and doesn’t follow the this behavior of strict mode. But here, despite strict mode, it returned undefined. Quite weird, isn’t it?

Yeah, of course it’s weird. But along with this, I also mentioned that an arrow function doesn’t have its own this context (the value of this). Instead, it depends on its lexical environment (where it is defined) meaning the surrounding environment. As we studied earlier, a normal function in strict mode returns undefined for this. In our case, the arrow function is nested within a normal function, so it inherits the value that its parent function has. That’s exactly what it does. The testParent() function had undefined as its this in strict mode, and that’s why we got undefined from your test() arrow function too.

I covered this point so you won’t be confused when you face it. Now, we need to move to the next step of this keyword, where it is used a lot, which is objects. Let’s quickly dive in and understand what the relationship of this keyword is to objects.

this in object

This is widely used in objects. Now we will deeply explore this, as we did with the rest of the things above. In a simple object, it uses this to refer to that object meaning the object where it is called. Check out this example:

JavaScript

copy

const obj = {
firstName: "John",
lastName: "Harry",
test() {
console.log(this); // output: {firstName: 'John', lastName: 'Harry', test: ƒ}
}
}

obj.test();

If you see, we called the function test() that is created within the object. It returns the whole object because within the function we have used console.log(this), which returns the same object since now it is no longer in the global scope, so it points to the object where it is called.

Before you go ahead, I want to clear that using the this keyword directly inside the object (outside any function) will still refer to the global object, like you can see below:

JavaScript

copy

const obj = {
  firstName: "John",
  lastName: "Harry",
  fullName: this,
};

console.log(obj.fullName); // output: Window {window: Window, self: Window, document: document, name: '', location: Location, …}

Ahh, one more confusion 😕, yes, but it needs a bit of a push to be understood entirely. As we know, the value of this in the global space is the window global object. So when we use this in an object outside any function, it means we are storing the original value of this as it is in the global scope. That is why it behaves the same as it does outside of the object or function.

But when we use it in a function inside the object, then as we call that function, the this keyword's value gets updated according to its calling context. And it returns the object it is called on. Check this example again, though I am using it twice:

JavaScript

copy

const obj = {
firstName: "John",
lastName: "Harry",
test() {
console.log(this); // output: {firstName: 'John', lastName: 'Harry', test: ƒ}
}
}

obj.test();

I hope the doubt is cleared for you. Now let us know how arrow functions behave in an object, and why we should avoid using them in the object and prefer a simple function. Let’s quickly start with this:

JavaScript

copy

const obj = {
firstName: "John",
lastName: "Harry",
testArrow: () => {
console.log(this); // output: Window {window: Window, self: Window, document: document, name: '', location: Location, …}
}
}

obj.testArrow();

Have you seen? We have used this within the arrow function in the object, but it returned the window global object rather than the object where it is defined. It’s quite weird, isn’t it? Yes, it is weird, but I will also uncover this mystery as I did with the rest of the weird stuff 😀.

Because arrow functions are designed not to have their own this. They capture this from the place they were defined, not from how they are called or what object they're in. But this question might arise in your mind: If arrow functions use lexical scoping for this, and the object is the surrounding scope, then why doesn't this refer to the object?

Yes, it is a great question if it came to your mind. Actually, an object is not a lexical scope in JavaScript. Yes, I am serious. Let’s understand how.

What is a lexical scope?

Lexical scope means “where the code is written — the surrounding function or module scope at the time the function is defined.”

  • Functions
  • Blocks ()
  • Modules

But: Objects do not create their own scope.

Have you seen? Objects don’t create a new lexical scope, so arrow functions defined inside them inherit this from the outer (usually global or function) scope, not the object itself.

But that doesn’t mean arrow functions are useless for handling this. Actually, they’re really helpful in situations like setTimeout(), where you want to keep the context from the outer scope. Let’s dive in.

Using setTimeout() Inside an Object Method (and How this Works)

Suppose you need to add some delay to a function that you’re using inside an object. As we know, arrow functions don’t behave as expected in this context, so we would prefer to use a regular function but that might lead to an issue we wouldn't expect. Let’s deeply understand this so that you can see what I’m saying.

Let’s start with a basic object:

JavaScript

copy

Let’s start with a basic object:
const user = {
  name: "John",
  greet() {
    console.log("Hello, I'm " + this.name);
  }
};

user.greet(); // Hello, I'm John

So far, so good. The this inside greet() refers to the user object because it's being called with user.greet().

Now Let's Add setTimeout()

Now suppose you want to delay the greeting by a second:

JavaScript

copy

const user = {
  name: "John",
  greet() {
    setTimeout(function () {
      console.log("Hello, I'm " + this.name);
    }, 1000);
  }
};

user.greet(); // After 1s → "Hello, I'm undefined"

Wait, what? Why is this.name undefined?

The function I passed to setTimeout() is a regular function, and when it runs, it does not inherit this from greet(). Instead, it's executed by the setTimeout system, which means this inside that function refers to the global object (or undefined in strict mode).

In other words, this lost track of the user object.

The Fix: Use an Arrow Function

Arrow functions don't have their own this. Instead, they use the value of this from the surrounding context, in this case, the greet() method, where this still refers to the user. This means that in an arrow function, this gets the value of its parent context, which, in our case, is greet(), a regular function.

JavaScript

copy

const user = {
  name: "John",
  greet() {
    setTimeout(() => {
      console.log(`Hello, I'm ${this.name}`);
    }, 1000);
  }
};

user.greet(); // After 1s → "Hello, I'm John"

Now it works! Because the arrow function doesn't redefine this, it keeps the one from greet() — which correctly points to user.

I didn’t originally want to cover this last section (the use of setTimeout within an object), but I realized it might be confusing when you need to handle a slight delay in methods. This issue is actually one of the biggest in JavaScript and is a major reason why arrow functions were introduced. I hope your understanding of this issue is now clearer.