JAW-DROPPING FUNDAMENTALS OF THE 'this' KEYWORD IN JAVASCRIPT.
Knowing the meaning, and practical applications of JavaScript's 'this' keyword.
JavaScript's this
keyword is an incredibly powerful concept. It most times tends to cause confusion among beginners or developers who don't understand how to use it, as well as contribute to unwanted bugs in our code. In this article, we will investigate what the this
keyword means, and three (3) magical methods which are essential for its application.
Prerequisite
Basic understanding of HTML and CSS.
Basic understanding of JavaScript and how it works behind the scenes.
What is the 'this' keyword?
The this
keyword is a special variable created for every function's execution context.
Behind the scenes in JavaScript, every function gets an execution context during execution. Each execution context comprises the variable environment, the scope chain, and the this
keyword.
You can think of the this
keyword as a pronoun in English. Let's take an example, My name is Aahil, I love to write. The "I" in the sentence is referring to "Aahil". That is what the this
keyword simply does, it refers to the function that is calling it.
The value of the created this
keyword, points to the owner of the function in which it is being used. In other words, when a function is executed, it gets its own distinct this
keyword, whose value is the function in which the this
keyword is used.
Unlike other variables, the this
keyword is not a STATIC variable. It gets its value depending on HOW the function is CALLED. Below are 3 ways by which functions are called in JavaScript 👇:
(1) As a Method 👇:
A function declared in an object is a method. The value of the this
keyword when used in a method, points to the object on which the method is called.
Let's take a code example.
const sample1 = {
name: "Aahil",
dateOfBirth: 1996,
calcAge() {
console.log(`${this.name} is ${2022 - this.dateOfBirth} years old`);
},
};
sample1.calcAge();
Let's break down the code sample. The following activities occurred above;
The object literal called
sample1
was created. It contains 2 properties,name
anddateOfBirth
, and a method calledcalcAge()
.The
calcAge()
method prints to the console of the browser a string composed of the name property and a calculated date of birth.The sample1 object calls the
calcAge()
method.
Output 👇:
The value of the this
keyword above is the object that calls the calcAge()
method which is sample1
. Therefore, this.name
is the same as sample1.name
which is the string 'Aahil'.
We can go ahead and confirm the value of the this
keyword. See the example below 👇
const sample1 = {
calcAge() {
console.log(this);
},
};
sample1.calcAge();
Output 👇:
As we can see above, the this
keyword points to the object sample1
.
(2) As a normal function call 👇:
When we create a function, it is expected that the function must be called before it runs. The value of the this
keyword when used in a function returns as undefined
when that function is called. This is true only for function expressions and function declarations but not arrow functions.
NOTE👉: This only applies when you're in strict mode, otherwise, the this
keyword points to the global window object.
Let's see this in action.
"use strict";
const sample2 = function () {
console.log(this);
};
sample2();
In the code snippet above, we explicitly set the script to execute in strict mode. We also have a simple function expression sample2()
which prints the this
keyword to the browser's console.
Output👇:
As earlier stated, the value of this
here is returned as undefined
in strict mode. Now let's see the effect of having the same code run in sloppy mode.
const sample2 = function () {
console.log(this);
};
sample2();
Output👇:
The output is the global window object - this is an object available in the global scope.
NOTE👉: Using strict mode is very essential because it prevents JavaScript from failing silently and we set strict mode by adding the string '' use strict '' at the beginning of the script file.
We cannot talk about using this
in function expressions and function declarations without mentioning its effect when used in ARROW FUNCTION.
As an arrow function 👇:
The arrow function is a simplified alternative for creating functions in JavaScript. Arrow functions don't get their own this
variable, however, it uses the this
keyword of its parent scope which is referred to as the lexical this
keyword.
Let's take a code example.
const sample3a = () => console.log(this);
sample3a()
In the example above, the arrow function is logging the this
keyword to the browser's console.
Output👇:
The result of running the code above returns the global window object. Remember, the value of the this
keyword when a function is called in sloppy mode is the global window object. This means, the arrow function doesn't have its own this
keyword, but it points to the this
keyword of the outer scope(parent scope).
We can observe the this
variable of the outer scope by just logging this
to the browser's console without it being inside any code block(or function).
console.log(this)
Output👇:
The this
variable of the outer scope is the global window object and that is why the this
variable of the arrow function above is pointing to the global window object. The image below gives a summary of this.
We can see the value of the this
keyword when the arrow function is put into another code block, in this case, another function.
const sample3b = function () {
const sample3bArrowFunction = () => {
console.log(this);
};
sample3bArrowFunction()
};
sample3b();
Let's break down the code snippet;
A function
sample3b()
which holds an arrow functionsample3bArrowFunction()
.sample3bArrowFunction()
simply logs to the console, thethis
variable.
Output👇:
The result of this example is undefined
. This is because the sample3bArrowFunction()
doesn't get it's own this
variable, it uses the this
keyword of the outer scope, which in this case is the function expression sample3b()
.
Remember from our background study, we established that function expressions return the value of this
as undefined
. Therefore, the this
keyword of sample3bArrowFunction()
will be undefined
as well.
NOTE👉: The arrow function is not a way of calling a function, but it is an important and special case that we have to put into consideration when studying this
.
(3) As Event listeners 👇:
When using event listeners in JavaScript, we select the element we want to listen to, then call the addEventListener()
method with 2 parameters. The first parameter is the event we are listening for and the second parameter is the function that gets triggered when that event is triggered.
The this
keyword inside the function that gets called when that event is triggered, points to the element which triggers that event. This is because in the document object model (DOM) all elements are objects, therefore, it would seem like an object was calling the function just like a normal method would.
Let's discuss this further with an example.
const buttonEl = document.querySelector('.btn')
const handleClick = function(){
console.log(this);
}
buttonEl.addEventListener('click', handleClick);
The following steps are carried out in the code snippet above;
(1) A variable buttonEl
holds the button HTML element.
(2) A function handleClick()
simply logs the this
keyword to the browser's console.
(3) The buttonEL
has an event listener attached to it, with 2 parameters. Which are;
The
click
event, which listens for when the button is clicked.The
handleClick()
callback function, is called only when the button is clicked.
Output👇:
The this
keyword points to the element calling the event, and in this case, the element calling the event is the button
element. This is why the output of our code snippet is the button
element.
NOTE 👉:
The value of the this
keyword is only assigned when the function is CALLED.
We have established that the this
keyword is not STATIC, which means it changes based on how a function is called. However, there are ways we can manually set the this
keyword. How cool is that, let's get into it.
The following three(3) magical ✨ methods are used to manually set the this
keyword;
(1) The Call() method 👇.
The Call()
method allows a function/method defined in an object to be used by another object without rewriting that method. By using the call()
method, you can write a function once and then use it on multiple objects.
We make use of the call()
method when we call a function. The call()
method accepts a list of arguments, where the first argument is an object, this will be the value of the this
keyword, and the rest are arguments pertaining to the function.
let's take a practical example.
function sample5() {
console.log(`${this.name} is ${this.age} old`);
}
const sample5Obj = {
name: "prince",
age: 24,
};
sample5.call(sample5Obj);
The following actions occurred above;
- A function
sample5()
simply prints to the console a name and an age. - An object
sample5Obj
holds two(2) properties, name, and age.
All this is nothing new at this point, until we call the function sample5()
using the call()
method, and we pass the object sample5Obj
as an argument.
Let's see the result of this example.
Output👇:
The output above takes the name and age variables from the sample5Obj
. This implies that the value of the this
keyword is pointing to sample5Obj
. This happens because we used the call()
method to set it explicitly.
Remember that calling the function without using the call()
method would have returned the value of this
as undefined
. Using the call()
method, we can assign the function sample5()
to multiple objects, we can see that below by adding another object.
function sample5() {
console.log(`${this.name} is ${this.age} years old`);
}
const sample5Obj1 = {
name: "Prince",
age: 24,
};
const sample5Obj2 = {
name: "Aahil",
age: 45,
};
sample5.call(sample5Obj1);
sample5.call(sample5Obj2);
Here, we added another object sample5Obj2
to show that the function can be used in multiple objects and the this
keyword would change depending on what object we pass into the call()
method.
Output👇:
The result of calling the function sample5()
on both objects changes because we are changing the first argument of the call()
method.
NOTE👇:
The syntax for using the call()
method is;
function.call(object, arg1, arg2,....,argn);
Where;
The first argument defines what the
this
keyword will beThe other arguments
arg1
,arg2
, andargn
defines the arguments of the function.
(2) The apply() method👇.
The apply()
method does the same thing as the call()
method, the only difference is that you pass the arguments of the function as an array instead of passing them directly.
Let's take a code example.
function sample6(name, age) {
console.log(this);
console.log(
`${name} is ${age} years old and the name of this object is ${this.objectName}`
);
}
const sample6Obj = {
objectName: "object1",
};
const args = ["aahil", 14];
sample6.apply(sample6Obj, args);
Let's break down the activities above;
(1) A function sample6()
with parameters of name and age. sample6()
logs to the browsers' console the this
keyword and a template string.
(2) An object sample6Obj
which contains a property objectName
.
(3) An array args
which holds 2 items.
(4) sample6()
is called using apply()
. This takes the sample6Obj
as the first argument, signifying that the value of this
would be sample6Obj1
, and it takes the args
array as the second argument.
Output👇:
Let's analyze our output now;
(1) The first output we have is the result of logging the this
keyword to the browser's console. The output is the object sample6Obj
which we set by using the apply()
method.
(2) The second output is the template string made up of elements from the args
array. Using apply allows us to pass our arguments into an array and pass the array as an argument when we call the function.
NOTE:👉 apply()
is not really in use now and the same pattern it uses can be done using the call()
method and the spread operator.
Let's see this below 👇.
sample6.call(sample6Obj, ...parameters);
The output of this code will give you the same result as earlier.
NOTE:👇
The syntax for using the apply()
method is;
function.apply(object, [args]);
The first argument defines what the value of this
would be and the other argument defines the array of arguments pertaining to the function.
(3) The bind() method 👇.
The bind()
method is used to set the this
keyword explicitly, just as the call()
and apply()
method. The only difference is that instead of calling the function immediately, it returns a new function with the this
keyword bound to it.
When we use the bind()
method, we can get a brand new function with the value of this
being whatever object we set it to. That function can now be used in multiple places.
Example👇:
function sample7(name, age) {
console.log(this);
console.log(
`${name} is ${age} years old and the name of this object is ${this.objectName}`
);
}
const sample7Obj = {
objectName: "object1",
};
const sample7Bind = sample7.bind(sample7Obj);
sample7Bind("aahil", 24);
We have the same syntax as before and the only difference is the function sample7Bind()
. This function is created when the bind()
method is used on the function sample7()
to explicitly set its this
keyword to the objectsample7Obj
.
sample7Bind()
is called with the arguments of the function with its this
keyword pointing to the object sample7Obj
.
Output👇:
The output is the same as when we used apply()
or call()
, the only difference here is that the bind method returns another function, and the apply()
and call()
methods just call the function directly.
NOTE 👉: A useful application of using bind()
is partial application. This is when some arguments of a function are passed into the bind method as default, then on calling the function, that argument would be skipped as it now serves as a default parameter of that function.
Conclusion
This is a very lengthy article, but I hope we achieved our goals for this article and we got a deeper understanding of how the this
keyword dynamically changes based on how functions are called and how we can explicitly determine the value of the this
keyword.
One key thing to note is that the this
keyword is simply a variable and whenever you're not sure of what the this
keyword is, you can simply log in to the browser's console to be on the safe side.
Thank you for reading 😊.