Binding +  Call, Apply and Bind in JavaScript

Binding + Call, Apply and Bind in JavaScript

Introduction

Everything works inside an Execution Context in JavaScript. Variables, functions, everything. A variable or a function has a scope when simply put means where they belong or what is their scope of action.

A fancy word or more accurate word is the Lexical Environment. As we know there are multiple execution contexts created when different functions are defined and invoked. For every execution context, a lexical environment is defined.

This may be a bit difficult to grasp in one go, but don't worry as we move forward you will understand things better.

Pre-requisites

Basic knowledge of how JavaScript works behind the scenes and basic JS syntax. Refer If Needed

Binding in JavaScript

Binding something in JavaScript means binding the identifier in a particular lexical environment. Each lexical environment is attached to a particular execution context and it binds the identifiers(variables or functions) to thethis keyword for that execution context.

Put in simpler terms it means that the ' this ' keyword will refer to which execution context it is bound to.

Note: The code where we deal with single execution contexts with the 'this' keyword, generally refers to the implicit binding. We will further see what is explicit binding too.

Enough Theory! Let's see some examples of implicit binding.

let personDetails= {
  firstName: 'Mahendra',
  middleName: 'Singh',
  lastName:'Dhoni',
  printFullName: function (){
    console.log(`My name is ${this.firstName} ${this.middleName} ${this.lastName}`);
  }
}
personDetails.printFullName();

What are we doing here?

We have declared an object with the name personDetails and have key-value pairs. The thing to notice is that we have a function as a key, yes we can use functions in an object? Remember, why? Because the function is first class citizens.

Here this is bound to the personDetails object. The reason is that we have invoked the printFullName function on the personDetails object. Btw function inside the object is called a method. So, if I say function or method, they are the same.

Let us see the output now:

Screenshot (695).png

As expected the full name is printed. Likewise, if we call the function on another object, this will refer to that object. An Example:

let personDetails2= {
  firstName: 'Rohan',
  middleName: 'Kumar',
  lastName:'Dubey',
  printFullName: function (){
    console.log(`My name is ${this.firstName} ${this.middleName} ${this.lastName}`);
  }
}
personDetails2.printFullName();

//Expected output: My name is Rohan Kumar Dubey

You can see here that only we change the object and this starts referring to some other execution context or object in that particular execution context.

Explicit Binding And Its Methods

Let me give you an example, sometimes back in school when we used to forget our stationery, we would borrow it from someone else. So, what we did here, rather than buying, maintaining, and bringing our stationery school, we borrowed and got rid of any of the steps involved.

Ah! That reminds us of our glory school days, doesn't it?👀

Anyways, the same thing we do in explicit binding, we borrow functions of one execution context from another. Without re-writing the entire function.

To achieve that we generally use three different methods call(), apply() and bind(). Let us see some examples now.

1. Decoding call() Method:

In the call method, we generally pass the object with which the function needs to be called. In other words, we give call the first parameter as the object which is from a different execution context and attach it to the function from other. This way we borrow the function from an execution context and use it in others.

Let us understand this with an example.

let bio = {
  firstName: "Rohan",
  lastName: "Dubey",
};

let bio2 = {
  firstName: "Tanay",
  lastName: "Pratap",
};

let printFullName = function () {
  console.log(`My name is ${this.firstName} ${this.lastName}`);
};

printFullName.call(bio);
printFullName.call(bio2);

In the example above, we are passing two different objects respectively to call as a parameter. When printFullName is called with object bio, the this keyword is bound to the execution context of the object bio.

And, when called with the object bio2, the this is bound to the execution context of the bio2 object.

Let's see the output now:

Screenshot (696).png

🥳Voila! The same output as we expected.

Now, what if we need to pass some other arguments. This could be achieved by simply passing them to the call method. Let's see how quickly.

Screenshot (697).png

But, don't you think passing arguments like this is a bit less scalable and does not makes sense. So, to solve this apply() method comes to the rescue.

2. Decoding apply() Method:

The only difference as I repeat the only difference between the call and apply method is the way we pass arguments. In the apply() method it allows us to pass values as an ArrayList. Let's see an example.

let bio = {
  firstName: "Rohan",
  lastName: "Dubey",
};

let bio2 = {
  firstName: "Tanay",
  lastName: "Pratap",
};

let printFullName = function (town, food, movie) {
  console.log(
    `My name is ${this.firstName} ${this.lastName}. I live in ${town} and I love ${food}. My favorite movie is ${movie}`
  );
};

printFullName.apply(bio, ["Jamshedpur", "Pizza", "Lucifer"]);
printFullName.apply(bio2, ["Bangalore", "Coconut-Water", "3 Idiots"]);

What we did here, it's simple, we just passed an array and not single values.

Pro-Tip: When you have only one argument to pass use call(), if more than that use apply().

Finally, it's time for our cool guy ' bind()'

3. Decoding bind() Method:

Now, I will show you with the help of code and output what the heck this bind is?

A simple question? Why do we use functions? Is it necessary?

The answer is simple, we use functions because the code becomes reusable, more readable, and modular. In the above two methods, what we were doing? We were simply calling the call or apply over the method and binding it to an object.

Unlike the call() and apply() methods, the bind() method doesn’t immediately execute the function. It just returns a new version of the function.

But, instantly calling it is not always a good idea. What if we need a copy of the result to use later? Here bind is the key to our needs.

Bind creates a copy of the function to invoke it at a later stage. An example:

let bio = {
  firstName: "Rohan",
  lastName: "Dubey",
};
let printFullName = function (town) {
  console.log(
    `My name is ${this.firstName} ${this.lastName} and I live in ${town}`
  );
};

let name = printFullName.bind(bio, "Jamshedpur");
name();

What are we doing here? We are using the bind() method to create a copy of the result we are getting. We have attached the bind method to the bio object and stored the returned value in the variable name.

Don't believe me? Let's console and see what is the name here?

console.log(name);

Screenshot (699).png

As you see it's a function. And we can invoke this function as and whenever we desire or need it. This is an example of function borrowing. And now let's finally see the output when we invoke the name.

Screenshot (700).png

Finally, we are done with the blog, but wait let's summarize what we learned.

Summary

  • In the case of implicit binding, this refers to the object on which the method is invoked.

  • When we need something from one execution context to use in another we do the explicit binding and it has different methods, out of which 3 important ones are call, apply and bind.

  • Call method is used to bind the this keyword with different objects as per our needs.

  • The only difference between call and apply is that apply takes an ArrayList instead of a single argument.

  • Bind returns a copy of the function which could be used anytime later when required.

Resources

Conclusion

We are done with this blog now. If your reaction was the same as above. Hit a like👍 button. If you are learning web development, follow me on Twitter

Until then, goodbye, we shall meet again with an interesting topic to explore.