Understanding the `this` Keyword in JavaScript: A Clear Guide with Tricky Scenarios


The this
keyword in JavaScript is one of the most powerful yet tricky aspects of the language. Its behavior can vary depending on how and where a function is called, which often leads to confusion among developers. In this article, we’ll break down the concept of this
, explore its typical use cases, and dive into some tricky scenarios with clear guidance on how to handle them.
What Is this
in JavaScript?
In JavaScript, this
is a context-sensitive keyword that refers to the object on which a function is executed. Its value is determined at runtime and is highly dependent on the call-site—the location in the code where the function is invoked.
Here’s a quick breakdown:
In the global context,
this
refers to the global object (window
in browsers orglobal
in Node.js).Inside an object method,
this
refers to the object.In a regular function,
this
can vary depending on whether the function is in strict mode.Arrow functions do not have their own
this
and inherit it from their surrounding context.
Typical Use Cases of this
1. In the Global Context
In the global scope, this
refers to the global object unless in strict mode, where it becomes undefined
.
Example:
console.log(this); // Outputs: Window (in browsers)
'use strict';
console.log(this); // Outputs: undefined
2. Inside Object Methods
When a function is called as a method of an object, this
refers to the object that owns the method.
Example:
const obj = {
name: 'JavaScript',
getName() {
return this.name;
}
};
console.log(obj.getName()); // Outputs: JavaScript
3. In Constructor Functions
When a constructor function is used to create an object, this
refers to the new instance of the object.
Example:
function Language(name) {
this.name = name;
}
const js = new Language('JavaScript');
console.log(js.name); // Outputs: JavaScript
4. With Classes
In ES6 classes, this
behaves similarly to constructor functions, referring to the instance of the class.
Example:
class Language {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
const js = new Language('JavaScript');
console.log(js.getName()); // Outputs: JavaScript
5. Arrow Functions
Arrow functions do not have their own this
and inherit it from the enclosing context. This behavior is particularly useful when working with callbacks.
Example:
const obj = {
name: 'JavaScript',
greet() {
const arrow = () => `Hello, ${this.name}`;
return arrow();
}
};
console.log(obj.greet()); // Outputs: Hello, JavaScript
Tricky Scenarios with this
Let’s explore scenarios where this
behaves in unexpected ways and how to address them.
1. Standalone Functions
When a function is detached from its object, this
no longer refers to the original object—it defaults to the global object (window
) or undefined
in strict mode.
Example:
const obj = {
name: 'JavaScript',
getName() {
return this.name;
}
};
const getName = obj.getName;
console.log(getName()); // Outputs: undefined
Solution: Use .bind()
to explicitly bind this
to the object.
const boundGetName = obj.getName.bind(obj);
console.log(boundGetName()); // Outputs: JavaScript
2. Inside Nested Functions
In a nested function, this
does not inherit the value from the outer function. Instead, it defaults to the global object or undefined
in strict mode.
Example:
const obj = {
name: 'JavaScript',
greet() {
function innerFunction() {
console.log(this.name);
}
innerFunction();
}
};
obj.greet(); // Outputs: undefined
Solution 1: Use an arrow function, which inherits this
from the surrounding context.
const obj = {
name: 'JavaScript',
greet() {
const innerFunction = () => console.log(this.name);
innerFunction();
}
};
obj.greet(); // Outputs: JavaScript
Solution 2: Store this
in a variable.
const obj = {
name: 'JavaScript',
greet() {
const self = this;
function innerFunction() {
console.log(self.name);
}
innerFunction();
}
};
obj.greet(); // Outputs: JavaScript
3. In Event Handlers
In event handlers, this
typically refers to the element that triggered the event. However, using an arrow function in this context changes the behavior.
Example:
document.querySelector('button').addEventListener('click', () => {
console.log(this); // Outputs: Window
});
Solution: Use a regular function to retain the element as this
.
document.querySelector('button').addEventListener('click', function () {
console.log(this); // Outputs: <button>
});
4. With setTimeout
In a setTimeout
callback, this
defaults to the global object.
Example:
const obj = {
name: 'JavaScript',
greet() {
setTimeout(function () {
console.log(this.name);
}, 1000);
}
};
obj.greet(); // Outputs: undefined
Solution: Use an arrow function to inherit this
.
const obj = {
name: 'JavaScript',
greet() {
setTimeout(() => {
console.log(this.name);
}, 1000);
}
};
obj.greet(); // Outputs: JavaScript
5. Detached Class Methods
When a class method is assigned to a variable, it loses its reference to the class instance.
Example:
class Language {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
const js = new Language('JavaScript');
const getName = js.getName;
console.log(getName()); // Outputs: undefined
Solution: Bind the method to the instance using .bind()
or use an arrow function.
class Language {
constructor(name) {
this.name = name;
this.getName = this.getName.bind(this);
}
getName() {
return this.name;
}
}
const js = new Language('JavaScript');
console.log(js.getName()); // Outputs: JavaScript
Summary
Understanding this
is crucial to writing clear and predictable JavaScript code. Here’s a quick guide:
Scenario | this Behavior | Solution |
Standalone Function | Defaults to global object or undefined | Use .bind() to explicitly set this . |
Nested Functions | Defaults to global object or undefined | Use arrow functions or store this . |
Event Handlers | Refers to event target | Use regular functions for handlers. |
setTimeout | Defaults to global object | Use arrow functions. |
Detached Class Methods | Loses reference to class instance | Use .bind() or arrow functions. |
By mastering these tricky parts and knowing when and how to manage this
, you can write robust and maintainable JavaScript code. 🎉
Subscribe to my newsletter
Read articles from Eklemis Santo Ndun directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Eklemis Santo Ndun
Eklemis Santo Ndun
Hi there, I'm a developer from Indonesia. I love learning new things especially technology. Now, not only do i love learning new things, but also to write about what i've learnt. In regular work day, i deal a lot with Images files, SQL Server, MS Access and Excel as well. To make my daily target reached with high quality and shorter time, i automate many of my task with help of Python. In some months where my regular task is not much, i spent time develop web apps to automate my colleagues regular task as well. Out of regular work time, i spend time learning and creating project with Javascript (and Html and CSS), learn UI/UX design with Figma, code Python scripts(Machine Learning), and Rust programming language. To help better remembering all things i've learnt, now i learn to write about them as well.