Taming the "this" Keyword:
JavaScript's Most Confusing Concept Demystified

Published on: March 26, 2025

JavaScript's this keyword is notorious for confusing developers at all levels.

In this guide, we'll explore the inner workings of the 'this' keyword in JavaScript and how it behaves in different contexts.

What the hell is 'this' anyway?

It is commonly used in object-oriented programming languages to differentiate between class attributes and method parameters with the same name, as well as to access the current object's properties and methods

1class Car {
2	// instance variables
3	type = 'hatchback';
4	
5	// constructor
6	constructor(type) {
7		// this.type refers to the instance variable
8		// type refers to the parameter or argument of the constructor
9		this.type = type;
10	}
11}

We don't need to use this keyword when there is no ambiguity.

1class Car {
2	model = 'Toyota';
3
4	constructor(modelArg) {
5		// There is no ambiguity here, so 'this' is not needed
6		model = modelArg;
7	}
8}
Where does 'this' get its value from?

In JavaScript, the this keyword is a reference to an object, but its value depends on how and where it is used (Execution Context).

Here are the general rules for where this points to:

Global Context

When used in the global context (outside of any function), this refers to the global object (window in browsers).

1console.log(this); // window 
2
3console.log(this === window); // true
4

This behavior changes in strict mode, where this is undefined instead of the global object.

1'use strict';
2
3console.log(this); // undefined
4
5console.log(this === window); // false
Function context

When used inside a function, this refers to the object that called the function.

1const getThis = function() {
2	console.log(this);
3}
4
5// window, since getThis is called in the global context
6getThis();

Diagram Loading...

Object context

When function is a property of an object, this refers to the object itself.

1const person = {
2	name: 'John',
3	sayName: function() {
4		console.log(this);
5		console.log(this.name);
6	}
7}
8
9// { name: 'John', sayName: [Function: sayName] }
10// John
11person.sayName();

Diagram Loading...

If the function is assigned to a variable, this refers to the global object. (since its not part of the object anymore)

1const { sayName } = person;
2
3// window
4// undefined
5sayName();

Diagram Loading...

Constructor context

When used inside a constructor function, this refers to the instance of the object being created.

1class Person {
2	name = 'John';
3
4	constructor(name) {
5		this.name = name;
6	}
7
8	getName() {
9		console.log(this);
10		return this.name;
11	}
12}
13
14const person = new Person('Jane');
15
16// { name: 'Jane', getName: [Function: getName] }
17console.log(person.getName()); // Jane
18

Diagram Loading...

However, if the class function is assigned to a variable, this refers to undefined, as it is not part of the class anymore.

1 const { getName } = person;
2getName(); // undefined

Diagram Loading...

JS executes classes in strict mode by default (mandated). This explains the difference in behavior between the "Function extracted from objects" and "Function extracted from classes" examples.

If the method is a static method, this refers to the constructor function (class) itself.

1class Person {
2	value = 10;
3
4	static getValue() {
5		console.log(this); // [Function: Person]
6		return this.value; // undefined
7	}
8}
9
10console.log(Person.getValue()); // undefined
Function constructor context

When used in a function constructor, this refers to the instance of the object being created.

1function Person(name) {
2	this.name = name;
3	this.getName = function() {
4		return this.name;
5	}
6}
7
8const person = new Person('John');
9person.getName(); // John

Don't forget to use the new keyword when creating a new instance of the object. Forgetting to use the new keyword will result in global object pollution.

1const person = Person('John');
2console.log(person); // undefined
3
4// global object pollution
5// console.log(window.name); if you are using a browser
6console.log(globalThis.name); // John
Event Handlers

When used as an event handler, this refers to the element that triggered the event.

1document.body.addEventListener('click', function () {
2	console.log(this); // <body>
3});

Diagram Loading...

Timers

When used as a callback function for a timer, this refers to the Timer object.

1setTimeout(function() {
2	console.log(this); // Timeout object
3});
Explicit Binding, Do what I say!
1const john = {
2	name: 'John',
3	sayName: function(arg1, arg2) {
4		console.log(this.name);
5		console.log({ arg1, arg2 });
6	}
7}
8
9const jane = {
10	name: 'Jane',
11};

If you want to explicitly set the value of this, you can use the following methods:

Call

Executes a function with the execution context of another object.

1john.sayName.call(jane, 'value1', 'value2');
2// Jane { arg1: 'value1', arg2: 'value2' }

Here sayHello() function is executed with the context of jane, instead of john.

Diagram Loading...

Apply

Similar to call(), but takes an array of arguments.

1john.sayName.apply(jane, ['value1', 'value2']);
2// Jane { arg1: 'value1', arg2: 'value2' }
Bind

Creates a new bounded function that, when called, has its this keyword set to the provided value.

1const boundFunction = john.sayName.bind(jane, 'value1', 'value2');
2boundedFunction();
3// Jane { arg1: 'value1', arg2: 'value2' }
Arrow Functions, to the rescue!

Javascript Arrow functions do not have their own this value. Instead, they inherit the this value from the enclosing lexical context.

Diagram Loading...

Behavior in Object Literals

Object literals do not create a new lexical scope. The this value in this case refers to the scope in which the object was declared.

1const person = {
2	name: 'John',
3	sayName: function() {
4		console.log(this);
5		console.log(this.name);
6	}
7}
8
9// { name: 'John', sayName: [Function: sayName] }
10// John
11person.sayName();
Behaviour in Constructor Functions and Classes
1class Person {
2	name = 'John';
3
4	constructor(name) {
5		this.name = name;
6	}
7
8	getName() {
9		return this?.name;
10	}
11
12	getNameArrow = () => {
13		return this?.name;
14	};
15}
16
17const person = new Person('Jane');
1function Person(name) {
2	this.name = name;
3
4	this.getName = function() {
5		return this.name;
6	}
7
8	this.getNameArrow = () => {
9		return this.name;
10	}
11}
12
13const person = new Person('John');

When getName() and getNameArrow() function are called using the person instance. They both have the same value of this.

  • getName() - gets the this value from the person instance due to object context.
  • getNameArrow() - gets the this value from the lexical context of the arrow function.
1person.getName(); // John
2person.getNameArrow(); // John

When getName() is called after being assigned to a variable, it loses its object context and this refers to the global object.

1const { getName, getNameArrow } = person;
2getName(); // undefined
3getNameArrow(); // John
Conclusion

Understanding the 'this' keyword is crucial for writing maintainable and bug-free code in JavaScript. It is a complex topic that requires practice to master.

By using arrow functions and explicit binding, you can avoid the confusion and pitfalls associated with the 'this' keyword.

In this article