DEV Community

Cover image for What is 'this' in JavaScript?
skaytech
skaytech

Posted on • Edited on • Originally published at blog.skay.dev

What is 'this' in JavaScript?

Introduction

The 'this' keyword is one such concept that has many layers to it and often a JavaScript newbie can find it overwhelming.

I can assure you that after going through this article, you'll be able to identify what 'this' means based on your function's execution context.

In this article, we will be covering the following topics:

Why does 'this' exist in JavaScript?

Before we get into the details, let us try and understand why 'this' exists in JavaScript. I think it'll be easier to explain it using a code example:

function sayHello(name) {
    console.log(`Hello ${name}`);
}
Enter fullscreen mode Exit fullscreen mode

The above is a function declaration of 'sayHello' that accepts 'name' as the argument. You'll not be able to say what will display on the console unless the function is invoked and a value is passed to the 'name' argument is passed to the function.

sayHello('Skay');

//Output -> Hello Skay
Enter fullscreen mode Exit fullscreen mode

When the function 'sayHello' is invoked with 'Skay', the output 'Hello Skay' displays on the console.

You can think of 'this' keyword behaviour to be very similar that of the above function argument example. The value of 'this' keyword will vary based on how the function is invoked.

Simply stated, the 'this' keyword exists in order to allow the user to decide which object should be focal when invoking a function or a method.

Implicit Binding

Implicit Binding rule states that in order to figure out what 'this' keyword references, first, look to the left of the dot of the function that is invoked.

Let us look at a simple example to figure out what the above means:

const user = {
  name: 'Skay',
  age: 38,
  sayHello: function () {
    console.log(`Hello ${this.name}`); // The 'this' keyword references the 'user' object
  },
};

//Note: Implicit Binding Rule
//To the left of the function call 'sayHello()' the user object is present
//Hence 'this' in this function invocation refers to the 'user' object
console.log(user.sayHello());
//Output -> Hello Skay
Enter fullscreen mode Exit fullscreen mode

Thing to Note:

  • The 'user' object contains the 'sayHello' function which references the 'this' keyword.
  • To the left of the function call 'sayHello' function is the user object and by implicit biding rule, the 'this' keyword references the 'user' object.
${this.name} -> gets translated to -> ${user.name}
Enter fullscreen mode Exit fullscreen mode

So, what happens when there isn't any dot to the left of the function call? That brings us to the next concept, the explicit binding.

Explicit Binding

Let us take the above example and move the function 'sayHello' out of the 'user' object.

const user = {
  name: 'Skay',
  age: 38,
};

//The function sayHello() moved out of the user object
const sayHello = function () {
  console.log(`Hello ${this.name}`);
};
Enter fullscreen mode Exit fullscreen mode

Now, how do we bind the 'user' object to the 'sayHello' function? We have the 'call' function to our rescue.

Call

'call' is a property of every function and the first argument you pass to 'call' will reference 'this' keyword within the function through Explicit binding.

//Using 'call' keyword, we are passing the 'user' object as an argument.
//This explicitly binds the 'user' object to the 'this' keyword
sayHello.call(user);
Enter fullscreen mode Exit fullscreen mode

As you can see from the above code, the 'user' object is explicitly bound to the 'sayHello' function using the 'call' property.

Apply

Let us take another example of the below function 'polyglot' that accepts arguments language1 and language2.

const user = {
  name: 'Skay',
  age: 38,
};

const polyglot = function (language1, language2) {
  console.log(
    `Hello, I'm ${this.name} and I know ${language1} and ${language2}`
  );
};
Enter fullscreen mode Exit fullscreen mode

Now, we know that using the 'call' property, we can bind the user object to the function explicitly and pass the arguments as shown below.

polyglot.call(user, 'JavaScript', 'Java');
//Output -> Hello, I'm Skay and I know JavaScript and Java
Enter fullscreen mode Exit fullscreen mode

Now, the 'apply' keyword gives you the flexibility to send multiple parameters using an array instead of individual values.

//Define the array that needs to be passed as arguments to the function
const languages = ['JavaScript', 'Java'];

//Use the apply keyword to explicitly bind the user object & pass the languages array
polyglot.apply(user, languages);
//Output -> Hello, I'm Skay and I know JavaScript and Java
Enter fullscreen mode Exit fullscreen mode

In other words, 'apply' is exactly same as 'bind' except for the fact that it provides you the flexibility to pass a single array which will spread each element of the array as arguments to the function.

Bind

So far, we have seen both 'call' and 'apply' keywords under explicit binding. The last one is 'bind', which is again exactly the same as 'call', but it returns a function that can be invoked at a later time than running it right away.

Let us look at the first example and find out how 'bind' keyword works with it.

const user = {
  name: 'Skay',
  age: 38,
};

const sayHello = function () {
  console.log(`Hello ${this.name}`);
};

//The 'bind' keyword returns a function instead of invoking the function immediately
const laterFn = sayHello.bind(user);

//The function is invoked to display the greeting on the console
laterFn(); //Output -> Hello Skay
Enter fullscreen mode Exit fullscreen mode

That concludes all the various ways of how to identify 'this' keyword with explicitly binding.

new Binding

In JavaScript, you can define a constructor function through which other objects can be created. Let us look at the below code example.

//Fruit is a constructor which accepts name and color as arguments
//Whenever the function 'Fruit' is invoked using the new keyword a new object is created
//The new object created will reference the 'this' keyword
const Fruit = function (name, color) {
  this.name = name;
  this.color = color;
  this.greet = function () {
    console.log(`Hello, I'm ${this.name} and my color is ${this.color}`);
  };
};

//Apple object will be created with the this.name & this.color referencing Apple & Red
const apple = new Fruit('Apple', 'Red');

//Banana Object will be created with the this.name & this.color referencing Banana & Yellow
const banana = new Fruit('Banana', 'Yellow');

//Greet function will be invoked with 'apple' reference
apple.greet(); //Output -> Hello, I'm Apple and my color is Red

//Greet function will be invoked with 'banana' reference
banana.greet(); //Output -> Hello, I'm Banana and my color is Yellow
Enter fullscreen mode Exit fullscreen mode

Things to Note:

  • When the constructor function 'Fruit' is invoked with the 'new' keyword, a new object will be created.
  • The 'this' keyword will naturally reference the newly created object during assignment.

Lexical Binding

Lexical binding is probably easier explained through a code example.

//A Constructor Function that accepts 'name' & 'hobby' array
const Person = function (name, hobbies) {
  this.name = name;
  this.hobbies = hobbies;
  this.display = function () {
        //hobbies array iterated through the map function
    this.hobbies.map(function (hobby) {
            //Inside this anonymous function, the 'this.name' cannot be found
            //Reason -> A new scope gets created within anonymous function
      console.log(`My name is ${this.name} & my hobby is ${hobby}`);
    });
  };
};

const hobbies = ['Sports', 'Music'];

const me = new Person('Skay', hobbies);

me.display();

/** Output
 * My name is  & my hobby is Sports
 * My name is  & my hobby is Music
 */
Enter fullscreen mode Exit fullscreen mode

Let us look at the above code example in detail:

  • The 'Person' is a constructor function that accepts both 'name' and 'hobbies' array.
  • We are invoking the constructor function with the name 'Skay' and the 'hobbies' array 'Sports' and 'Music'.
  • When the 'display' function is invoked, the hobbies.map function is invoked.
  • There's an inner anonymous function within the hobbies.map function which creates it's own scope and hence when '${this.name}' is encountered, it tries to search for 'name' within the new context and does not find it.
  • Hence the output does not print the 'name' field.

Ideally, the expectation would have been lookup to the parent scope and find the 'name' variable within the Person function. This was addressed when ES6 arrow function was created.

However, we can still address this problem by using the 'bind' keyword as shown in the code example below.

Using bind

The example is exactly the same as above, but we are using the 'bind' keyword to explicitly bind the 'this' keyword to the anonymous function. If you run the function, you should see the 'this.name' would be properly referenced and the output printing the name as 'Skay'.

const Person = function (name, hobbies) {
  this.name = name;
  this.hobbies = hobbies;
  this.display = function () {
    this.hobbies.map(
      function (hobby) {
        console.log(`My name is ${this.name} & my hobby is ${hobby}`);
            //By 'explicit' binding the 'this' object the 'this.name' will be referenced
      }.bind(this)
    );
  };
};

const hobbies = ['Sports', 'Music'];

const me = new Person('Skay', hobbies);

me.display();

/** Output
 * My name is Skay & my hobby is Sports
 * My name is Skay & my hobby is Music
 */
Enter fullscreen mode Exit fullscreen mode

ES6 - Arrow Function

The same code can be simply addressed by using an Arrow Function. This is recommended and preferred and is compatible with all modern browsers.

We just removed the 'function' keyword and replaced it with the 'arrow' symbol to make it an arrow function. If you like to read more, here's a detailed article on arrow functions.

const Person = function (name, hobbies) {
  this.name = name;
  this.hobbies = hobbies;
  this.display = function () {
    this.hobbies.map((hobby) => {
      console.log(`My name is ${this.name} & my hobby is ${hobby}`);
    });
  };
};

const hobbies = ['Sports', 'Music'];

const me = new Person('Skay', hobbies);

me.display();

/** Output
 * My name is Skay & my hobby is Sports
 * My name is Skay & my hobby is Music
 */
Enter fullscreen mode Exit fullscreen mode

Window Binding

Finally, we come to the window binding which is probably also referred to as the global context.

function displayFruit() {
  console.log(`Hello ${this.fruit}`);
}

displayFruit();

//Output -> Hello Undefined
Enter fullscreen mode Exit fullscreen mode

In the above code example, there's no dot operator and hence no implicit binding happened. Also, since call, apply, bind were used to explicitly bind the function with an object, the 'this' keyword get bound to the global object which is the 'window' object.

If you define 'fruit' variable to the window object and run the above code, it'll print the name of the fruit as shown below.

function displayFruit() {
  console.log(`Hello ${this.fruit}`);
}

window.fruit = 'Peach';

displayFruit();

//Output -> Hello Peach
Enter fullscreen mode Exit fullscreen mode

Strict Mode

If you have the โ€œstrict modeโ€ enabled, then the JavaScript will NOT allow the reference of the 'this' keyword to default to the window object. It will just keep โ€œthisโ€ as undefined as shown in the code example below.

'use strict'

function displayFruit() {
  console.log(`Hello ${this.fruit}`);
}

window.fruit = 'Peach';

displayFruit();

//Output -> Hello undefined
Enter fullscreen mode Exit fullscreen mode

Conclusion

That's it folks! We've covered all that's there to find out how to identify the 'this' object in JavaScript.

So, whenever you see a 'this' keyword inside a function, then you'll apply the following rules:

  1. Check where the function was invoked.
  2. If there is an object to the left of the dot, then, that's what the โ€œthisโ€ keyword is referencing. If not, continue to the next step.
  3. If the function was invoked with a โ€œcallโ€, โ€œapplyโ€, or โ€œbindโ€ keword, then itโ€™ll explicitly state what object the โ€œthisโ€ keyword is referencing to. If not, continue to the next step.
  4. Was the function invoked using the โ€œnewโ€ keyword? If so, the โ€œthisโ€ keyword is referencing the newly created object. If not, continue to the next step.
  5. Is โ€œthisโ€ inside of an arrow function? If so, its reference may be found lexically in the enclosing (parent) scope. If not, continue to the next step.
  6. If you are in 'strict mode', then โ€œthisโ€ keyword is undefined. If not, continue to next step.
  7. The โ€œthisโ€ keyword defaulted to the global โ€œwindowโ€ object.

Hope you enjoyed this article! Do let me know your comments and feedback on the same.

You might also be interested in:

Top comments (9)

Collapse
 
aktorion profile image
Georgios

In the last example the output wouldn't be :
//Output -> Hello Peach

Instead it would be :
//Output -> Hello undefined

Collapse
 
skaytech profile image
skaytech

Thanks for catching it! I've updated the code ๐Ÿ™‚๐Ÿ™

Collapse
 
leob profile image
leob

Great explanation, very clear! You could write a follow-up post to talk about prototypical inheritance and about the ES6 "class" keyword.

Collapse
 
skaytech profile image
skaytech

Yep! That's in my list of things to write about ๐Ÿ™‚ Why is it that we have only 24 hours in a day?? ๐Ÿ˜› Thanks for reading the article!

Collapse
 
ankitdevelops profile image
Ankit Kumar

Thanks for this amazing article. I am learning Javascript and I was so confused with "this" keyword. Thanks for making it easy.

Collapse
 
skaytech profile image
skaytech

I'm glad you found it useful. Thanks for reading my article...please share for maximum reach. ๐Ÿ™

Collapse
 
steevn profile image
steevn

Thank you. Im new in JavaScript and 'this' is driven me crazy. But I keep learning and reading.
Such a great Community. <3

Collapse
 
skaytech profile image
skaytech

Thank you for reading my article ๐Ÿ™

Collapse
 
kingleo10 profile image
Niroj Dahal

THIS made sense. :)