Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
June 20, 2020 03:51 pm GMT

Why We Need 'this' in JavaScript and How To Know It's Value

As you build more and more projects using JavaScript, youll discover theres no getting away from the this keyword. Its present in virtually every context in the language. Youll encounter it when youre:

  • Using methods of regular objects
  • Referencing values within classes
  • Trying to access an element or event in the DOM

this may have felt like a confusing part of the language or at least one that you dont quite understand as you should. This lesson is here to serve as your guide to grasping the this keyword once and for all, what it means in different contexts and how you can manually set what it is equal to.

Note that you will likely forget whats covered in this article here from time to time, and thats okay. All JavaScript developers have trouble at one point or another with understanding this, so dont hesitate to come back to this lesson if you need a refresher.

this is a reference to an object

What is this? Lets try to get at the simplest definition of this possible:

Simply put, this, in any context, is a reference to a JavaScript object. But what makes it tricky, is that the object that this refers to can vary. Its value varies according to how a function is called.

Thats what makes it a tricky conceptit is a dynamic characteristic thats determined by how the function is called. For example, whether it is called as an arrow function or function declaration, as a normal function or as a method, as a function constructor or as a class, or within a callback function.

Why this?

I think a big part of why developers dont fully grasp this is because they dont understand why we need it at all.

One of the main reasons this dynamically changes based on how the function is called is so that method calls on objects which delegate through the prototype chain still maintain the expected value. In other words, so features can be shared across objects.

Unlike many other languages, JavaScripts this being dynamic is essential for a feature called prototypical inheritance, which enables both constructor functions and classes to work as expected. Both of these types of functions play a large role in making JavaScript apps, so this is an immensely important feature of the language.

Four rules to know out what this refers to

There are four main contexts where this is dynamically given a different value:

  1. in the global context
  2. as a method on an object
  3. as a constructor function or class constructor
  4. as a DOM event handler

Lets go through each of these contexts one by one:

Global context

Within an individual script, you can figure out what this is equal by console logging this.

Try it right now and see what you get.

console.log(this); // window

In the global context, this is set to the global object. If youre working with JavaScript in a web browser, as we are, this is the window object. Again, as we mentioned, this always refers to an object.

However, you know that functions have their own context as well. What about for them?

For function declarations, it will still refer to the window object:

function whatIsThis() {  console.log(this); // window}whatIsThis();

However this behavior changes when we are in strict mode. If we put the function in strict mode, we get undefined:

function whatIsThis() {  "use strict";  console.log(this); // undefined}whatIsThis();

This is the same result as with an arrow function:

const whatIsThis = () => console.log(this); // undefinedwhatIsThis();

Now why is it an improvement for this to be undefined when working with functions, both with function declarations in strict mode and arrow functions, instead of the global object, window? Take a minute and think why this is better.

The reason is that if this refers to the global object, it is very easy to add values onto it by directly mutating the object:

function whatIsThis() {  // "use strict";  // console.log(this); // undefined  this.something = 2;  console.log(window.something);}whatIsThis(); // 2

We never want data that is scoped to a function to be able to leak out into the outer scope. That contradicts the purpose of having data scoped to a function altogether.

Object method

When we have a function on an object, we have a method. A method uses this to refer to the properties of the object. So if we have a user object with some data, any method can use this confidently, knowing it will refer to data on the object itself.

const user = {  first: "Reed",  last: "Barger",  greetUser() {    console.log(`Hi, ${this.first} ${this.last}`);  },};user.greetUser(); // Hi, Reed Barger

But what if that object is then nested inside another object? For example if we put user in an object called userInfo with some other stuff?

const userInfo = {  job: "Programmer",  user: {    first: "Reed",    last: "Barger",    greetUser() {      console.log(`Hi, ${this.first} ${this.last}`);    },  },};userInfo.personalInfo.greetUser(); // Hi, Reed Barger

The example still works. Why does it work?

For any method, this refers to the object is on, or another way of thinking about it, the object that is the immediate left side of the dot when calling a method. So in this case, when calling greetUser, the object personalInfo is on the immediately left side of the dot. So thats what this is.

If however, we tried to use this to get data from the userInfo object:

const userInfo = {  job: "Programmer",  user: {    first: "Reed",    last: "Barger",    greetUser() {      console.log(`Hi, ${this.first} ${this.last}, ${this.job}`);    },  },};userInfo.personalInfo.greetUser(); // Hi, Reed Barger, undefined

We see that this doesnt refer to userInfo. The rule here is to look on the immediate left hand side of the dot when calling a method and youll know what this is.

Constructor functions + classes

When you use the new keyword, it creates an instance of a class or constructor function, depending on which youre using. When a class is instantiated with new, the this keyword is bound to that instance, so we can use this in any of our class methods with confidence knowing that we can reference our instance properties, such as in this example, first and age:

class User {  constructor(first, age) {    this.first = first;    this.age = age;  }  getAge() {    console.log(`${this.first} age is ${this.age}`);  }}const bob = new User("Bob", 24);bob.getAge(); // Bob's age is 24

Since we know that how classes under the hood is based off of constructor functions and prototypical inheritance, we know the same rule will apply to constructor functions as well:

function User(first, age) {  this.first = first;  this.age = age;}User.prototype.getAge = function () {  console.log(`${this.first}'s age is ${this.age}`);};const jane = new User("Jane", 25);jane.getAge(); // Jane's age is 25

DOM event handler

In the browser, there is a special this context for event handlers. In an event handler called by addEventListener, this will refer to event.currentTarget. More often than not, developers will simply use event.target or event.currentTarget as needed to access elements in the DOM, but since the this reference changes in this context, it is important to know.

In the following example, well create a button, add text to it, and append it to the DOM. When we log the value of this within the event handler, it will print the target.

const button = document.createElement("button");button.textContent = "Click";document.body.appendChild(button);button.addEventListener("click", function (event) {  console.log(this); // <button>Click me</button>});

Once you paste this into your browser, you will see a button appended to the page that says Click. If you click the button, you will see <button>Click</button> appear in your console, as clicking the button logs the element, which is the button itself. Therefore, as you can see, this refers to the targeted element, which is the element we added an event listener to.

Explicitly setting the value of this

In all of the previous examples, the value of this was determined by its contextwhether it is global, in an object, in a constructed function or class, or on a DOM event handler. However, using the functions call, apply, or bind, you can explicitly determine what this should refer to.

.call() and .apply()

Call and apply are pretty similarthey all you to call a function on a certain context. Again, this refers to an object. For example, say we have an object whose values we want to use for a function:

const user = {  name: "Reed",  title: "Programmer",};function printUser() {  console.log(`${this.first} is a ${this.title}.`);}printUser(); // "undefined is a undefined"

At this point, the function and object have no connection. But using call or apply, we can call the function like it was a method on the object:

printUser.call(user);// or:printUser.apply(user);

We can see how call and apply set the this context with the following code, again using our whatIsThis function:

function whatIsThis() {  console.log(this);}whatIsThis.call({ first: "Reed" }); // { first: Reed}

In this case, this actually becomes the object passed as an argument.

Passing arguments to .call() and .apply()

But what if you want to use a function that requires parameters to work? Such as this:

const user = {  name: "Reed",  title: "Programmer",};function printBio(city, country) {  console.log(`${this.name} works as a ${this.title} in ${city}, ${country}.`);}printBio.call(user);

If you try to use call as before, you see were setting the this context for the function, but we need to pass arguments with call as well.

We can do so by providing those arguments after the this argument, separated by commas:

printBio.call(user, "New York City", "USA");

This is where apply differs, however. The only difference between call and apply is that it takes the additional arguments in the form of an array:

printBio.apply(user, ["New York City", "USA"]);

.bind()

Both call and apply are one-time use methodsif you call the method with the this context it will have it, but the original function will remain unchanged.

Sometimes, you might need to use a method over and over with the this context of another object, and in that case you could use the bind method to create a brand new function with an explicitly bound this.

const userBio = printBio.bind(user);userBio();

In this example, every time you call userBio, it will always return the original this value bound to it. Attempting to bind a new this context to it will fail, so you can always trust a bound function to return the this value you expect.

const userBio = printBio.bind(user);userBio();const user2 = {  name: "Doug",  title: "Entrepreneur",};userBio.bind(user2);userBio();

Although this example tries to bind userBio once again, it retains the original this context from the first time it was bound.

Arrow functions have no this

Arrow functions do not have their own this binding. Instead, they go up to the next execution context.

const user = {  first: "Bob",  fn() {    console.log(this.first);  },  arrowFn: () => {    console.log(this.first);  },};user.fn(); // Bobuser.arrowFn(); // undefined

Summary

Lets review the four different ways of calling a function that determine its this binding:

  1. in the global context: refers to global object or undefined in strict mode / for arrow fn
  2. as a method on an object: refers to object on left side of dot when method called
  3. as a constructor function or class constructor: refers to the instance itself when called with new
  4. as a DOM event handler: refers to the element itself

When in the global scope or context, this is the global object, usually window, in non-strict mode, and undefined for strict mode and arrow functions.

For a method on an object, which is what this was largely designed to help with, when calling it look to the immediate left hand side of the dot. Thats the object this is bound to.

For a constructor on functions or classes, using new will automatically bind this to the created instance, so all methods added to the prototype can use those instance properties.

And finally for a normal function, not an arrow function, pass to a DOM event handler (addEventListener), this refers to the DOM element itself

Just follow these rules and youll always be able to demystify what this is!

Want To Become a JS Master? Join the 2020 JS Bootcamp

Join the JS Bootcamp Course

Follow + Say Hi! Twitter Instagram reedbarger.com codeartistry.io


Original Link: https://dev.to/codeartistryio/why-we-need-this-in-javascript-and-how-to-know-it-s-value-1595

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To