JAW-DROPPING FUNDAMENTALS OF THE  'this' KEYWORD IN JAVASCRIPT.

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 and dateOfBirth, and a method called calcAge().

  • 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 👇:

This Keyword result1.JPG

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

This keyword image1.jpg

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 👇:

Capture1.JPG

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👇:

This Keyword result2.JPG

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👇:

This Keyword result3.JPG

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👇:

This Keyword result4.JPG

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👇:

This Keyword result5.JPG

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.

This keyword image2.jpg

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 function sample3bArrowFunction().

  • sample3bArrowFunction() simply logs to the console, the this variable.

Output👇:

This Keyword result6.JPG

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👇:

This Keyword result7.JPG

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👇:

This Keyword result8.JPG

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👇:

This Keyword result9.JPG

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 be

  • The other arguments arg1, arg2, and argn 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👇:

This Keyword result10.JPG

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👇:

This Keyword result11.JPG

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