Solving ‘this’ Keyword Confusion in JavaScript

Sujitha Sakthivel
CodeX
Published in
7 min readSep 6, 2024
Solving ‘this’ Keyword Confusion in JavaScript

Imagine you’re at a party. You start chatting with a group of people, and someone across the room waves at you. You wave back, thinking they’re acknowledging you — but then you realize they are actually greeting someone behind you. Awkward, right? That moment of confusion — thinking the gesture was meant for you when it wasn’t — is exactly what happens when developers first encounter the this keyword in JavaScript.

In life, context is everything. Misunderstanding it leads to awkward situations, just like misinterpreting this in JavaScript can lead to confusing bugs. In this article, we’ll explore why this behaves differently depending on the context and how to prevent these misunderstandings from happening in your code.

Let’s also picture this: You’re working on a JavaScript project, and everything seems to be going smoothly — until you hit a snag. You’re using a callback function, but suddenly, your variables are out of place, and nothing is behaving as expected. After hours of debugging, you realize that the issue boils down to a single, seemingly simple keyword: this.

If you’ve ever been confused by the this keyword in JavaScript, you’re not alone.

It’s one of the most common pain points for developers, especially when dealing with callbacks or arrow functions.

In this article, we’ll break down exactly how this works in different contexts and, more importantly, how to avoid the traps that can lead to frustration.

Table of Contents

  1. What is the this keyword?
  2. How this works in different contexts
  3. Common pitfalls and confusion with this
  4. How to control this
  5. Strict mode and this
  6. Best practices and how to avoid this confusion

What is the this Keyword?

In JavaScript, the this keyword is one of the core concepts that many developers initially find confusing, especially because its behavior changes depending on where and how it's used. At its essence, this is a reference to the execution context—the current environment in which the code is being executed. But what does this really mean?

Think of this as a pointer that helps JavaScript keep track of the object that is currently responsible for executing a piece of code. Depending on how and where a function is invoked, this may point to different things—sometimes it refers to the global object, sometimes to an object you're working with, and sometimes it can even be undefined. The behavior of this is determined dynamically, based on the way the function is called, which can be a source of confusion.

The purpose of this is to provide a way for functions and methods to refer back to the object they belong to. It allows developers to write reusable code, where the same function can behave differently depending on the object that invokes it.

Simplified Example:

  • In the Global Context: When a function is executed in the global scope, this typically refers to the global object (e.g., window in browsers or global in Node.js).
  • In Object Methods: When a method (a function inside an object) is called, this refers to the object that contains the method.
  • In Constructors: When using constructors with the new keyword, this points to the newly created object.

How this works in different contexts

1. Global Context:

Scenario: You’re working from home, and suddenly, someone knocks on your door. You answer it, and it’s the mailman with a package. The mailman doesn’t know who the package is for, so he says, “Here’s your package.” But because you’re the one who answered the door, you assume the package is for you, right?

Explanation: In the global context, this refers to the global object (like window in the browser). It’s like answering the door in your house—there’s no specific object, so this defaults to the global environment.

function getPackage() {
console.log(this); // In browsers, `this` refers to the `window` object
}
getPackage();

2. Object Method Context:

Scenario: Imagine you’re the manager of a team. During a meeting, you say, “I’ll handle the client’s project.” Because you’re the manager, everyone knows that “I” refers to you in this context.

Explanation: When this is used inside an object method, it refers to the object itself. Like the manager owning the responsibility, the method is owned by the object, so this points to that object.

const manager = {
name: "Alex",
role: "Manager",
assignTask: function () {
console.log(this.name + " is assigning a task.");
}
};
manager.assignTask(); // "Alex is assigning a task."

3. Constructor Function Context:

Scenario: Imagine you’re at a car factory, building a new car. You assemble it and put the badge on it that says “This is a Tesla.” In this context, you’re building something new, so you get to define what “this” refers to (in this case, a new car).

Explanation: When you use this inside a constructor function, it refers to the newly created object. Just like you're responsible for naming the new car, this refers to the specific instance you’re creating.

function Car(model) {
this.model = model;
}
const myCar = new Car('Tesla');
console.log(myCar.model); // "Tesla"

4. Event Handler Context:

Scenario: You’re hosting a party, and someone taps you on the shoulder. You turn around because the tap was meant for you — the person interacting with you is specifically signaling you.

Explanation: In an event handler, this refers to the element that triggered the event, like the person who tapped you at the party. It doesn’t refer to the global context but the specific element involved in the interaction.

const button = document.querySelector('button');
button.addEventListener('click', function () {
console.log(this); // Refers to the button element
});

5. Arrow Function Context:

Scenario: Imagine you’re hanging out with your friends, and your sibling shows up. They shout, “Hey, come here!” But since you’re with your friends, they assume your sibling is calling them too. In this case, your friends are just tagging along with you, adopting the same context.

Explanation: Arrow functions don’t have their own this. Instead, they inherit this from the surrounding code. Like your friends tagging along, the arrow function follows the this context of where it’s written.

const group = {
leader: "Chris",
introduce: function () {
const arrowFunc = () => {
console.log(this.leader); // Inherits `this` from the introduce method
};
arrowFunc();
}
};
group.introduce(); // "Chris"

Common Pitfalls and Confusion with this

  1. Event Listeners: When you use this inside an event listener (like a button click), it refers to the element that triggered the event. For example, if you click on a button, this will point to that specific button.

Example:

button.addEventListener('click', function() {
console.log(this); // Refers to the clicked button
});

2. Callbacks and this Binding: When passing a method as a callback, this can unexpectedly point to the global object (or undefined in strict mode) instead of the object it originally belonged to. This happens because the context of this gets lost when the method is executed separately from its object.

Example:

const car = {
model: "Tesla",
showModel: function() {
console.log(this.model);
}
};
setTimeout(car.showModel, 1000); // `this` is undefined here, not `car`

Solution: Use bind() or arrow functions to ensure this keeps its proper context.

How to Control this

  1. call(), apply(), and bind()
    These methods let you manually control what this points to, making sure it behaves as you expect.
  • call(): Lets you immediately call a function and explicitly set the value of this.
  • apply(): Works like call(), but it takes an array of arguments.
  • bind(): Creates a new function with this permanently set to a specific value, so when it’s called later, this won’t change.

Example:

const person = { name: 'Alice' };
function greet() {
console.log(this.name);
}
greet.call(person); // Outputs: "Alice"

2. Using Arrow Functions to Avoid this Pitfalls
Arrow functions don't have their own this; instead, they inherit this from the surrounding context. This makes them super useful in situations like callbacks or promises where you want to avoid unexpected changes to this.

Example:

const car = {
model: "Tesla",
logModel: function() {
setTimeout(() => {
console.log(this.model); // `this` stays with the car object
}, 1000);
}
};
car.logModel(); // Outputs: "Tesla"

Arrow functions save you from this confusion, especially in async code, while call(), apply(), and bind() give you control over this when you need it!

Strict Mode and this

In strict mode, this behaves differently:

  • Global Context: In regular mode, this in the global scope refers to the global object (like window). In strict mode, this is undefined.
  • Function Context: When a function is called without an explicit object (like in a simple function call), this is also undefined instead of referring to the global object.

This means that strict mode helps prevent accidental global variables and provides a safer way to manage this.

Best Practices to Avoid this Confusion

We all know how frustrating it can be when working on a group project at school. If everyone tackles their part without communicating, you might end up with overlapping tasks or missing sections. To keep things running smoothly, you need to assign clear roles and responsibilities to each team member, so everyone knows who is doing what.

Key Takeaways:

  1. Use Arrow Functions: They inherit this from the surrounding context, helping to prevent confusion in callbacks.
const student = {
name: 'Bob',
study: function() {
setTimeout(() => {
console.log(`${this.name} is studying`); // `this` refers to student
}, 1000);
}
};

2. Use bind(): When passing methods as callbacks, use bind() to explicitly set this.

const teacher = {
subject: 'Math',
teach: function() {
console.log(`Teaching ${this.subject}`);
}
};
setTimeout(teacher.teach.bind(teacher), 1000); // `this` refers to teacher

Arrow functions save you from this confusion, especially in async code, while call(), apply(), and bind() give you control over this when you need it!

By clearly defining roles in your project, you can avoid confusion — just like using the right techniques helps clarify this in your code!

--

--

Sujitha Sakthivel
CodeX
Writer for

Technical Writer | Skilled in simplifying complex tech topics!😎