Let's set 'this' ourselves

Let's set 'this' ourselves

In my previous blog, I've discussed the different ways 'this' value is set depending on the way we invoke it.

To recap,

  • If we invoke it on a constructor function - it gets set to the newly created object

  • If we invoke it on a method - it gets set to the object itself.

  • If we invoke it on a function - it gets set to the global object 'window'

In this blog, let's discuss how we can set the value of 'this' by ourselves and letting the power ✊ be in our hands.

call()

call() is a method directly invoked onto a function. The way we use 'call()' is by passing in a single value which would want to be set to 'this', followed by arguments which the receiving function requires.

Let's consider a simple example below

function add(num1, num2){
    return num1+num2;
}
//simple function invoke
add(3, 2)
//5

//using call method
add.call(window, 3, 2);
//5

Let's break this down, the function add is invoked by window object which we explicitly mentioned as the value of 'this', followed by the two arguments which the add function required. The two ways of calling the add function resulted in the same values and we might not understand the true power of call method, so let's look at another example.

const person = {
  name: "John",
  type: "person",
  sayGoodThings: function () {
     console.log(`${this.name} is a good `${this.type}`);
   }
}

person.sayGoodThings();
//John is a good person

The above example is a typical way of calling a method and the this having the value of object the method is associated with. But, what if we wanted to use the sayNiceThings function again on a different object? We can make that happen using the call method.

const student = {
  name: "Tina",
  type: "student"
}

person.sayGoodThings.call(student);
//Tina is a good student

So, this is where the power of call really shines in, it lets us "borrow" a method from one object and then use it for another object.

apply()

Similar to call(), apply() as well is used to call on a function and also specify the value of this. However, the different is, instead of passing the arguments one by one like we did in call, apply() takes the functions arguments in an array.

Let's look at the same examples again but using apply()

function add(num1, num2){
    return num1+num2;
}
//simple function invoke
add(3, 2)
//5

//using call method
add.apply(window, [3, 2]);
//5

In apply(), we take all the arguments required for the function in an array and then we pass the entire array into apply()

const person = {
  name: "John",
  type: "person",
  sayGoodThings: function () {
     console.log(`${this.name} is a good `${this.type}`);
   }
}

person.sayGoodThings();
//John is a good person

const student = {
  name: "Tina",
  type: "student"
}

person.sayGoodThings.apply(student);
//Tina is a good student

By the above example, we are now convinced that both call() and apply() yield results same way but with the difference in way arguments are sent over.

So, the next question you would be thinking of is when would you choose one over the other?

call() comes with a limitation i.e., if you don't know the number of arguments the function needs. apply() can be used in this scenario because it takes an array of arguments and unpacks them to pass to the function. The unpacking comes at a teeny performance cost, so keep that in mind.

bind()

Similar to call() and apply(), bind method also allows us to specify the value of 'this'. bind is method which is also called on a function but instead of invoking the function right away, bind returns a new function with a specified 'this'.

Let's look at an example to understand

function growTwice(cb) {
   cb();
   cb();
}

const myAge = {
  age: 20,
  growOneYear: function () {
    this.age += 1;
  }
};

dog.growOneYear();
//21

growTwice(dog.growOneYear);
//21

Although the growOneYear was called twice in the growTwice function, the result was still 21 because the 'this' here was being referred to the global object - window.

Let's use bind() for the above example to yield the result we're expecting

const calculateAge = dog.growOneYear.bind(dog);
growTwice(calculateAge)
//22

Remember, bind() returns a new function and so we stored it in calculateAge and sent that over as a callback function to growTwice()

Summary

  • call() is invoked on a function by specifying the value of 'this' and has arguments passed in individually separated by commas

  • apply() works same as call() but differs in the way the arguments are passed i.e., we pass them in an array

  • bind() is invoked on a function with the 'this' specified and it returns a brand new function, which allows us to call it like a normal function