call(), apply() and bind() - Three incredibly important JavaScript methods

call(), apply() and bind() - Three incredibly important JavaScript methods

What's the difference between call(), bind() and apply()? How to use them? How does "this" keyword work differently with these methods? What is function borrowing? Why they are not easy to wrap up in the head?

Most Javascript programmers have these confusing questions every time they come across these methods. Well, let's put an end to all the confusion with the help of this article.

In this article, you will learn about the call(), apply() and bind() methods, and how to use them in javascript. These methods are been asked almost in every front-end interview. So, are you excited to learn these amazing methods in the simplest way? Let's go!

Prerequisites

This article assumes the reader has the basic knowledge of "this" keyword.

Introduction

Let's first understand a basic overview of "this" keyword:

"this" refers to the current object the code is been written inside. By default, "this" points to the "window" object (at the global level). Also, "this" is determined by how a function is called (run-time binding).

Let's try to understand "this" keyword with an example:

Suppose, we have an object - "name" ->

const name = {
    firstName: "Manoj",
    lastName: "Asarsa",
    displayName: function () {
        console.log(this.firstName + " " + this.lastName);
    }
}

name.displayName();

Here, the "this" inside the name object will point to the name object as it is defined inside the function scope and not global scope. Also, the function of name object is called. As a result, this.firstName and this.lastName are accessing the firstName and lastName. Therefore, the output of this will be:

Manoj Asarsa

This was basic right? Let's make it a little interesting.

Suppose, we have another object, say "name2" :

const name2 = {
    firstName: "Aditya",
    lastName: "Roy",
}

Now, there's a challenge for you: What if we want to display the full name of the object name2?

Well, there are two ways to do so:

(i) Write same displayName() inside name2 object and call it as we did for name object earlier.

(ii) Using Function Borrowing (using call(), bind() and apply() methods).

Well, the first approach isn't a good one and not recommended.

We are more interested in the second one. But, hold on. What it mean by borrowing function? Let's understand.

In simple words, borrowing functions from objects and using them with the data of some other objects.

Function Borrowing works in two simple steps:

(a) Take the function that needs to be called. i.e.

name.displayName()

(b) Use any of these methods(call, bind and apply) with this function. for ex:

name.displayName.call(name2)

image.png

Let's ride along with them one by one and see how we can solve the above challenge as well.

1. call () method

Key points to remember about the call() method:

  • First argument - It's a REFERENCE of an object, where the "this" will point to.

  • Later arguments - These are the arguments to the function that is been called.

functionToBeCalled.call(firstArg, laterArgs....);

Let's solve the above challenge now with the help of the call() method.

name.displayName.call(name2);

By writing like this, we will get the full name for the "name2" object as well.

Here, we are borrowing the displayName() method from name object and using it with the data of name2 object and printing the full name.

So, the output will be:

Aditya Roy

Now, let's extract out the displayName() function outside of name object and make it separate function. Also, let's add some arguments for displayName(), say city and state.

const name = {
    firstName: "Manoj",
    lastName: "Asarsa",
}

// extracted out the displayName()

const displayName = function (city, state) {
    console.log(this.firstName + " " + 
    this.lastName + " from " + city + ", " + state);
}

const name2 = {
    firstName: "Aditya",
    lastName: "Roy",
}

// Now, we access the function "displayName()" directly,
// as it's not a part of "name" object

displayName.call(name, "Mount Abu", "Rajasthan");   // Manoj Asarsa from Mount Abu, Rajasthan
displayName.call(name2, "Panaji", "Goa");      // Aditya Roy from Panaji, Goa

The last two lines of output will be:

Manoj Asarsa from Mount Abu, Rajasthan
Aditya Roy from Panaji, Goa

As we can see above, the laterArgs (city and state args) are been passed to function displayName().

Similarly, if we have more args to function, then we can pass them as comma-separated in the call() method.

2. apply () method

Key points to remember about the apply() method:

  • Similar to the call() method, the only difference is "the way we pass the laterArgs to function".

  • In apply(), we pass the laterArgs in a list or array instead of passing args as comma-separated.

functionToBeCalled.apply(firstArg, [laterArgs....]);

Let's see with an example:

const name2 = {
    firstName: "Aditya",
    lastName: "Roy",
}

const displayName = function (city, state) {
    console.log(this.firstName + " " + 
    this.lastName + " from " + city + ", " + state);
}

displayName.apply(name2, ["Panaji", "Goa"]);  // Aditya Roy from Panaji, Goa

As you can see, the second argument of apply() is a list of arguments. This is the only difference between the call() and apply() methods.

3. bind () method

Key points to remember about the bind() method:

  • Similar to the call() method, but with one big difference. The call() method calls the function (displayName) directly, whereas, bind() method binds the function (displayName) with the object (that is passed as firstArg to bind).

  • RETURNS - A copy of that function (that can be called later, instead of directly calling it).

image.png

Let's see what the bind() method looks like?

const returnedFunction = functionToBeCalled.bind(object, laterArgs....);
returnedFunction();

Let's see with an example:

displayName.bind(name2, "Panaji", "Goa");

As we read the bind() method does not execute the function right away. Instead, it creates and returns a bound function that can be executed later. So, we need to store the returned function in some variable. Let's do that:

const printDetails = displayName.bind(name2, "Panaji", "Goa");

Now, this "printDetails" stores the returned function from bind() method. Now, this can be called later on at any time in the program. Let's try to call it and see what output it gives:

printDetails();   // Aditya Roy from Panaji, Goa

Similarly, You can use bind() to create a function bound to an object. This way no matter when and how it’s called, it’s called with the object it is tied to.

And that's all folks!

Here's a live link for the above code: codesandbox.io/s/call-apply-and-bind-method..

Conclusion

  • call(), apply(), and bind() methods are used to couple a function with an object. This way you can call the function on the object as if it belonged to it.
  • The call() and apply() are very similar methods. They both execute the bound function on the object immediately.
  • The bind() method does not execute the function right away. Instead, it creates and returns a bound function that can be executed later.

Congratulations! 🎉🤩 You have learned the three most powerful methods in javascript. I hope the next time if any of the questions come from these three methods in interviews, you can shine into it.