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

js
class Car {
	// instance variables
	type = 'hatchback';
	
	// constructor
	constructor(type) {
		// this.type refers to the instance variable
		// type refers to the parameter or argument of the constructor
		this.type = type;
	}
}

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

js
class Car {
	model = 'Toyota';

	constructor(modelArg) {
		// There is no ambiguity here, so 'this' is not needed
		model = modelArg;
	}
}

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).

js
console.log(this); // window 

console.log(this === window); // true

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

js
'use strict';

console.log(this); // undefined

console.log(this === window); // false

Function context

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

js
const getThis = function() {
	console.log(this);
}

// window, since getThis is called in the global context
getThis();

Diagram Loading...

Object context

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

js
const person = {
	name: 'John',
	sayName: function() {
		console.log(this);
		console.log(this.name);
	}
}

// { name: 'John', sayName: [Function: sayName] }
// John
person.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)

js
const { sayName } = person;

// window
// undefined
sayName();

Diagram Loading...

Constructor context

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

js
class Person {
	name = 'John';

	constructor(name) {
		this.name = name;
	}

	getName() {
		console.log(this);
		return this.name;
	}
}

const person = new Person('Jane');

// { name: 'Jane', getName: [Function: getName] }
console.log(person.getName()); // Jane

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.

js
 const { getName } = person;
getName(); // 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.

js
class Person {
	value = 10;

	static getValue() {
		console.log(this); // [Function: Person]
		return this.value; // undefined
	}
}

console.log(Person.getValue()); // undefined

Function constructor context

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

js
function Person(name) {
	this.name = name;
	this.getName = function() {
		return this.name;
	}
}

const person = new Person('John');
person.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.

js
const person = Person('John');
console.log(person); // undefined

// global object pollution
// console.log(window.name); if you are using a browser
console.log(globalThis.name); // John

Event Handlers

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

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

Diagram Loading...

Timers

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

js
setTimeout(function() {
	console.log(this); // Timeout object
});

Explicit Binding, Do what I say!

js
const john = {
	name: 'John',
	sayName: function(arg1, arg2) {
		console.log(this.name);
		console.log({ arg1, arg2 });
	}
}

const jane = {
	name: 'Jane',
};

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.

js
john.sayName.call(jane, 'value1', 'value2');
// 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.

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

Bind

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

js
const boundFunction = john.sayName.bind(jane, 'value1', 'value2');
boundedFunction();
// 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.

js
const person = {
	name: 'John',
	sayName: function() {
		console.log(this);
		console.log(this.name);
	}
}

// { name: 'John', sayName: [Function: sayName] }
// John
person.sayName();

Behaviour in Constructor Functions and Classes

js
class Person {
	name = 'John';

	constructor(name) {
		this.name = name;
	}

	getName() {
		return this?.name;
	}

	getNameArrow = () => {
		return this?.name;
	};
}

const person = new Person('Jane');
js
function Person(name) {
	this.name = name;

	this.getName = function() {
		return this.name;
	}

	this.getNameArrow = () => {
		return this.name;
	}
}

const 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.
js
person.getName(); // John
person.getNameArrow(); // John

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

js
const { getName, getNameArrow } = person;
getName(); // undefined
getNameArrow(); // 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.