Learn this Keyword in JavaScript the Easy Way

Learn this Keyword in JavaScript the Easy Way

What is the infamous this in JavaScript, implicit and explicit binding? Learn about it in this post.

Β·

5 min read

🚨 Be cautious - Don’t compare this keyword of JavaScript with other programming languages. The working of JavaScript is inherently different from that of object-oriented programming languages like C++, Java, OR 🐍 Python.

Let’s start with the anatomy of this.

https://media.giphy.com/media/xT9Igznmre6tnKH1Bu/giphy.gif

Anatomy of this

A function’s this keyword refers to the execution context for that function call. Note that, the function call and not the function was defined. 🚨 It doesn’t matter how the function was defined, what will be this keyword entirely depends on how the function was called.

Therefore, a this aware function can have a different context each time it’s called. This is amazing. It makes this keyword very flexible and reusable. It’s somewhat JavaScript’s version of dynamic scope. Read more about the difference between lexical scope and dynamic scope in the Lexical Scope section of Get an in-depth view of Scopes in JavaScript article.

Consider the following code:

function eat(person) {
  console.log(`${person} is eating ${this.food}`);
}

function restaurant() {
  var foodContext = {
    food: "πŸ•πŸ•πŸ•",
  };

  eat.call(foodContext, "John");
}

restaurant(); // John is eating πŸ•πŸ•πŸ•

Here, eat is a this aware function. In the restaurant function, we use the call method on eat function for binding the this keyword of eat with a context which in this case is foodContext. This is called hard binding (more on this later in the post).

Since during the call, eat function’s this refers to the foodContext, we have this.food as πŸ•πŸ•πŸ•. This is mind-blowing because now you can use the eat function with multiple contexts. πŸ”₯ this provides flexibility that lexical scopes don’t provide.

function eat(person) {
  console.log(`${person} is eating ${this.food}`);
}

function restaurant() {
  var breakfast = { food: "🍞🍞🍞" };
  eat.call(breakfast, "Breakfast");

  var lunch = { food: "πŸ₯—πŸ₯—πŸ₯—" };
  eat.call(lunch, "Lunch");

  var dinner = { food: "πŸ₯˜πŸ₯˜πŸ₯˜" };
  eat.call(dinner, "Dinner");
}

restaurant();

// Output:
// Breakfast is eating 🍞🍞🍞
// Lunch is eating πŸ₯—πŸ₯—πŸ₯—
// Dinner is eating πŸ₯˜πŸ₯˜πŸ₯˜

What is hard binding? Well, there are 4 different ways of invoking a function and each one of them will give a different this context.

https://media.giphy.com/media/Y3AQNvFtEM2rBNqPdv/giphy.gif

Implicit Binding

What happens when you don’t explicitly bind this keyword to a context?

// Namespace pattern
var restaurant = {
  food: "πŸ“πŸ“πŸ“",
  eat(person) {
    console.log(`${person} is eating at ${this.food}`);
  },
};

restaurant.eat("🐡"); // 🐡 is eating at πŸ“πŸ“πŸ“

Here, this.food is πŸ“πŸ“πŸ“ not because the eat function is inside the restaurant object, so this points to the restaurant. It’s because, in the call site, eat is invoked with the restaurant object.

The detail that this keyword depends on how the function is called (manner of the function call) and not how it’s defined is very important because if we were to use a callback in this scenario then the call site would be different and hence this keyword would be pointing on a different object.

// Namespace pattern
var restaurant = {
  food: "πŸ“πŸ“πŸ“",
  eat(person) {
    console.log(`${person} is eating at ${this.food}`);
  },
};

function personEating(person, callback) {
  callback(person);
}

personEating("🐡", restaurant.eat);

Here, this keyword is pointing to the personEating context. So if we had [this.food](http://this.food) inside the personEating function then this.food won’t be undefined. For example:

function anotherPersonEating(person, callback) {
  this.food = "πŸ‰πŸ‰πŸ‰";
  callback(person);
}

anotherPersonEating("🐡", restaurant.eat); // 🐡 is eating at πŸ‰πŸ‰πŸ‰

This raises another issue while using callbacks, we lose our this. This issue is solved by hard binding. Having implicit binding helps to share methods (behavior) among different contexts. It’s an intentional tradeoff between predictable and flexible. To share behaviors let’s learn about explicit binding.

https://media.giphy.com/media/YO3icZKE2G8OoGHWC9/giphy.gif

Explicit Binding

We can use call OR apply methods on a function to explicitly tell JavaScript, which context to invoke the function in.

function eat(person) {
  console.log(`${person} is eating ${this.food} in ${this.name}`);
}

var mcDonalds = {
  name: "McDonalds",
  food: "πŸ”πŸŸπŸŸ",
};

var pizzaHut = {
  name: "Pizza Hut",
  food: "πŸ•πŸ•πŸ§ƒ",
};

// Using .call
eat.call(mcDonalds, "John"); 
// John is eating πŸ”πŸŸπŸŸ in McDonalds

// Using .apply
eat.apply(pizzaHut, ["John"]); 
// John is eating πŸ•πŸ•πŸ§ƒ in Pizza Hut

A variation of explicit binding is hard binding. Here we solve the issue of losing this binding. This issue commonly happens when working with callbacks. Read more on this in the Implicit Binding section.

var pizzaHut = {
  name: "Pizza Hut",
  food: "πŸ•πŸ•πŸ§ƒ",
  eat(person) {
    console.log(`${person} is eating ${this.food}`);
  },
};

setTimeout(pizzaHut.eat, 1000, "🐡"); // 🐡 is eating undefined

What happens is that the call site is different, it is inside the setTimeout, and depending on its call site our this behaves because of which we get undefined (since this.food won’t be defined there). To solve this issue we use the bind method on a function which will produce a new function that will be bound to the context we provide. 🚨 This will take away the flexibility offered by this keyword.

Also, note that apply and call will also give the same results but instead of returning a new function, they will just execute the function in hand. πŸ“‹ On a side note, the setTimeout utility is defined by HTML, it’s not invoking it just with the default call, it actually explicitly invokes it with a .call in the context of global. So the this points to the global object for the setTimeout.

🚨 Note on the flexibility provided by this keyword

If we were to go to all the trouble to define a bunch of functions on some namespace object and have this in front of every property reference and every method access, AND then if all of your function calls use the bind, then we would be cutting yourself off at the knees ❌.

This is because you put all the this keywords for its dynamic feature and then use bind to remove that dynamic feature. A better thing to do in this case will be to write a module that uses closure and has a fixed, predictable behavior.

Conclusion

this keyword provides flexibility that lexical scope and closures don’t. We can opt-out of this flexibility by hard binding but be cautious to not overdo it because it dilutes the purpose of using this. This is the end of this post but not for this keyword top. There are some topics such as the new keyword, default binding, binding precedence, and this inside an arrow function. So to not miss anything do πŸ”₯ follow me and if you like the post then don’t forget to give it a πŸ‘πŸΌ.

https://media.giphy.com/media/l3q2GD8H7y2lpgFTq/giphy.gif

Β