Comprehensively understanding the “this” keyword in JavaScript12 min read
The this
keyword might be one of the most confusing and elusive parts in JavaScript because of its behavior in different contexts compared with other languages. In this article, we are going to learn how to use the this
keyword properly and comprehensively understand what is this
.
Table of Contents
Before we start
In order to understand the this
keyword in JavaScript, there are a few points we first need to consider. Firstly, the this
keyword in JavaScript is a keyword, not a value, the this
keyword’s value has nothing to do with the function itself, how the function is called determine the this
value. Secondly, JavaScript is a scripting language, which means when you write some JavaScript code, the interpreter (not the compiler) reads the code and execute it line by line and the environment contains the line that is being executed is known as the execution context. And the this
keyword might behave differently based on execution context – which is lied on the top of the stack where those execution contexts are being maintained.
The default “this” context
Let’s examine the default this
value by opening up your web browser, then right-click choose the Inspect Element then go to the tab Console, or type Command + Shift + J
(on macOS) or Ctrl + Shift + J
(on Windows) to open the console, then when you get the console ready, type the following code:
console.log(this);
Then, hit enter, what you see? Yeah, you see the Window object. By default in JavaScript, the this
value will refer to the root object or the Window object. The same rule is applied when we doing a simple function call:
function myFunction() {
console.log(this); // [object Window]
}
myFunction();
When we run this function, it also gives us the same result that we did in the first piece of code, the this keyword refers
to the global object which is the Window object. But when we enter the strict mode, the this
value might be different, consider the code below:
function anotherFunction() {
'use strict'
return this;
}
anotherFunction() === Window // false
Comparing this function with the Window object, it doesn’t equal anymore. Because when we entering the strict mode and it realizes we just make a direct call to the anotherFunction()
which isn’t a method or a property of an object (e.g Window.anotherFunction()
) hence this comparison will return false. But instead, if we compare our function call with undefined
, the result would be true:
anotherFunction() === 'undefined' // true
In general, in a global context or in simple function call without strict mode, the this
value will always refer to the Window object ([object Window]).
“this” within the Objects
Object Literal
There are a few ways to create an object in JavaScript. With Object Literal, we simply create an object with a pair of curly brackets. Inside the Object Literal, the this
value will always refer to its own object, not the Window object:
const obj = {};
obj.someMethod = function() {
console.log(this); // obj
}
obj.someMethod();
In the code fragment above, we first create an object literal named obj
, then we create a method (functions that are stored in object properties are called “methods”) named someMethod
and inside this method, we console the this
value, and finally we call someMethod
then we get the result is the object itself because the Window object didn’t invoke the function, but our obj
object did.
You might wonder, why it returns {someMethod: ƒ}
, truthfully because it’s our obj
object, it has one method which is someMethod
, if you type obj
in the console and enter, the result would be the same.
Object Constructor / Function Constructor
The same rule is also applied to the object constructor, the this
value will refer to the object itself where the function is called:
function MyConstructorObject() {
this.someMethod = function() {
console.log(this); // MyConstructorObject
}
}
const obj2 = new MyConstructorObject();
obj2.someMethod(); // MyConstructorObject
Why it returns MyConstructorObject
and not obj2
simply because the object itself here is the MyConstructorObject
, the obj2
just holds the reference to this object.
New Object Instances
With Function Constructor, as we just saw in the example above, we can create many objects based on this function constructor, and this function constructor works as a blueprint for creating new objects. When we invoke this function with the new
keyword, we actually create a new instance from this function, and the this
value will always refer to the newly created instance of this object. For example:
function Worker(workerName, workerAge, workerSalary) {
this.name = workerName;
this.age = workerAge;
this.salary = workerSalary;
this.getInfo = function() {
console.log(`Name: ${this.name}, Age: ${this.age}, Salary: ${this.salary}`);
console.log(this);
}
}
let a = new Worker("Nam", 20, 100);
a.getInfo();
let b = new Worker("Andy", 24, 200);
b.getInfo();
Output:
Name: Nam, Age: 20, Salary: 100
Worker { name: 'Nam', age: 20, salary: 100, getInfo: [Function] }
Name: Andy, Age: 24, Salary: 200
Worker { name: 'Andy', age: 24, salary: 200, getInfo: [Function] }
So as you can see, the this
value inside the getInfo()
function behaves differently. In the first case, when we call a.getInfo()
, the this
value refers to the instance assigned to the variable a
. And b.getInfo()
, the this
value refers to the instance stored in the variable b
. We also have have another console.log(this)
to point out the this
value will refer to the object, in this case, the instance which calls the getInfo()
function.
“this” with Events
This is the most interesting part when working with the this
keyword, because when we handle events with JavaScript, the this
keyword can be dynamic and the value could be changed greatly on the same function.
Let us create a simple HTML file to demonstrate the dynamic of this
keyword:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic This</title>
</head>
<body>
<div class="elem">
<p>This keyword in JavaScript</p>
</div>
<button class="btn">Click me!</button>
<script>
const a = document.querySelector('.elem');
const b = document.querySelector('.btn');
function myFunction() {
console.log(this);
}
a.addEventListener('click', myFunction); // <div class="elem">...</div>
b.addEventListener('click', myFunction); // <button class="btn">Click me!</button>
myFunction(); // [object Window]
</script>
</body>
</html>
We’ve created a div that contains a paragraph and a simple button, then in between the script tags we write some JavaScript code. We first select element belonged with class elem
and store it to the variable a
, here this element is our div
element, then we do the same thing to store the button
element to our b
variable. On top of that, we create a simple function myFunction
then we use addEventListener
for both variable a
and b
, pass into them the click
events and the same myFunction
function.
When we click to the div
element, the console will print out this element, because this
value will refer to the owner of this event, and in this case, it’s the div
element, the same rule is applied to the button
element. The last call is just identical as what we did before, we call the myFunction
function, which hasn’t been attached to any object, hence, by default, we will see [object Window] in the console.
“this” in Arrow Function
With the introduction of the arrow function as one of the ES6’s features. There is a new way to write functions in JavaScript concisely. But before we dive in the this
keyword in the arrow function, let’s examine one example:
const person = {
name: "Nam",
langs: ['JavaScript', 'Java', 'Python', 'Haskell'],
tasks: function() {
this.langs.forEach(function(lang) {
console.log(`${this.name} wants to learn ${lang}`);
});
}
}
person.tasks();
/*
undefined wants to learn JavaScript
undefined wants to learn Java
undefined wants to learn Python
undefined wants to learn Haskell
*/
What happened with Nam? Why this.name
is undefined
? Nam might be a cursed name then it cannot be printed out. You might expect that because the this
keyword is inside a method in an object, definitely it should refer to the object contains this method, which is person
. But actually do you notice the forEach
method? This method accepts a callback function, in our case, it’s an anonymous function, then our this
value is inside this function. And theoretically the this
value inside this function also wrapped by the method tasks
should be treated as belonging to this method, right? In JavaScript, no.
This is another remarkable point that we need to comprehend. The same old story when the this
keyword is inside a function alone, this
value will refer to the Window object. If the this
value lies inside a function and this function is inside a method, the same rule also is applied as we can observe.
How to solve that? How can we bring the name to the console? There are several answers. One we can use the arrow function:
const person = {
name: "Nam",
langs: ['JavaScript', 'Java', 'Python', 'Haskell'],
tasks: function() {
this.langs.forEach(lang => {
console.log(`${this.name} wants to learn ${lang}`);
});
}
}
person.tasks();
Output:
Nam wants to learn JavaScript
Nam wants to learn Java
Nam wants to learn Python
Nam wants to learn Haskell
The problem simply can be solved with the arrow function. In JavaScript ES6, the arrow function uses lexical scoping, which means it doesn’t bind it owns this
, instead, it inherits one from the parent scope, which is our method tasks
. this
always refer to the same object of its parent which is outside the function.
However, if we use the arrow function as a method in an object, it has no binding scope and it inherits from the parent one which is the Window object, which leads to an unexpected result:
const obj = {
someMethod: () => {
console.log(this);
}
}
obj.someMethod(); // [object Window]
Changing the “this” context with bind(), call() and apply()
With some of the examples above, there are a few problems arise that we want to change the context of a function. Functions in JavaScript are first-class objects which means they have their own methods. Thankfully JavaScript already has several methods that can help us accomplish our goal which are call()
, apply()
and bind()
methods.
Call() and Apply() methods
The call()
and apply()
methods are almost identical with respect to their usage. The fundamental difference between call()
and apply()
is the call()
method accepts an argument list, while apply()
accepts a single array of arguments. The syntax might be:
/** Call method */
myFunction.call(anotherScope, arg1, arg2); // argument list
/** Apply method */
myFunction.aplly(anotherScope, [arg1, arg2]); // array of arugments
If we create a simple function and invoke this function by the call()
method and don’t pass to this method any argument, it simply refers to the Window object by default:
function myFunc() {
console.log(this);
}
myFunc.call(); // [object Window]
Now, for example, we have a function that references this
but has no this
context and we want to use this function to print out an object’s property:
function myFunction() {
console.log(`My name is ${this.name}`);
}
const person = {
name: "Nam",
age: 19
}
myFunction() // My name is undefined
Actually, we cannot do person.myFunction()
because this function isn’t a method of the object, so how we can get the name of this object from the function? We can use the call()
method, like this:
myFunction.call(person); // My name is Nam
myFunction.apply(person); // My name is Nam
The first argument you pass to call will be what the this
keyword inside that function is referencing, in our case, the this
keyword will refer to our object, person
.
In case we have an array with a number of items and we want to use this array with our object, it works perfectly with the call()
method, for example myFunction.call(anotherScope, arr[0], arr[1], arr[2], arr[3]
but as you can see it’s quite cumbersome. Like aforementioned, the apply()
method accepts an array as the second argument, so it comes in handy when we just need to pass a single array instead of writing a single item in this array:
function myFunction(a, b) {
console.log(`My name is ${this.name} and I know ${a}, ${b}.`);
}
const person = {
name: "Nam",
age: 19,
}
const langs = ['JavaScript', 'Java'];
myFunction.apply(person, langs); // My name is Nam and I know JavaScript, Java.
Bind() method
Sometimes, you might need to use a method over and over with the this
context of another object. This might be ideal to use the bind()
method, this method creates a new function that, when called, has its this
keyword set to the provided value and it retains the original this
context from the first time it was bound. It perhaps a little bit dubious for now, let’s take an example to clarify this:
const person = {
name: "Nam",
age: 69
}
function getInfo() {
console.log(`My name is ${this.name} and I'm ${this.age} years old!`);
}
const a = getInfo.bind(person);
a(); // My name is Nam and I'm 69 years old!
const anotherPerson = {
name: "Daniel",
age: 24
}
a.bind(anotherPerson);
a(); // My name is Nam and I'm 69 years old!
As we can see in the example, we first bind the function to the object person
, it works fine as call()
or apply()
methods that give us the value inside the object, again we want to bind the function to another object second times nonetheless it retains the original this
context from the first time it was bound. So we call the function twice and get the same result.
Summary
In order to understand this
keyword in JavaScript takes a little time and patient. To sum up, this
in JavaScript is a keyword, not a value, the function itself doesn’t determine the value of this
but instead how function is called does. In the global context or simple function call without strict mode, the this
value will always refer to the Window object. Inside an object, the this
value refer to the owner object, the same rule applies when creating new object instance, or with function constructor. The value of this
can be dynamic when working with events. With arrow functions, this
has no binding and it inherits from the parent scope, which called lexical scope. To change the context of a function, we can either use call()
, apply()
or bind()
methods.