by Kevin Kononenko
If you want to write clear code that you (or a teammate) can re-read at a later date, here is one common rule: don’t repeat yourself!
If you create repetitive methods or functions, your code will be harder to maintain going forward. You will create bugs simply by failing to remember to update multiple versions of the same code.
In order to write cleaner code, you can use the apply, call, and bind methods to purposefully manipulate execution context. Different objects can share methods without rewriting them for each individual object.
Apply, call, and bind are sometimes called function methods, since they are called alongside a function.
How is this like cooking, exactly?
These three methods are kind of like applying cooking skills to prepare food for a cookout. Think of the different contexts that you might need to cook:
- A general meal that you can cook pretty much any time and make everyone happy (pasta and sauce)
- A cookout that might also be a party (burgers, hot dogs etc.)
- A fancy dinner for just you and your partner (fish and wine)
- Making dessert for a potluck event (cake)
Each one of these requires a different set of cooking techniques. Some are unique to an individual context, while others are more generalized. I will explain more in a minute.
In this case, each cooking context is kind of like an object. If you say that you are going to be cooking out on the grill, for example, that implies that you have a few skills… like operating a grill!
So, if we have an individual method for each of the cooking techniques you might use, there will be some unique methods to each object, and some cases where a method can be applied to multiple objects.
In the code above, boiling water is a generalized skill that can probably be applied in any context.
Let’s use an example. The grill() method is within the context of the cookout object. That means that if you are holding a cookout, you expect that you will need to call up those grill skills.
But wait. You don’t forget how to use the grill when the cookout ends! Let’s say that you are your partner want to cook a steak for a fancy dinner, like the fancyDinner object. You still want to be able to borrow that grill() method from the cookout object. That is where apply, call, and bind come in.
This relationship between cooking skills (methods) and cooking contexts (objects) will be the main way that I show how to use apply, call, and bind().
An Introduction to the Bind Method
Let’s imagine that you are holding a cookout for your son or daughter’s 10th birthday party. You want to cook three types of meat on the grill to satisfy everyone: chicken, burgers, and steak. They are apparently all meat eaters at this party.
However, you have no idea what each individual person wants! So you are going to need to ask each attendee when they arrive at the party. Each type of meat generally requires the same steps:
- Add seasoning
- Put it on the grill
- Remove from grill after a certain amount of time
So there is no point in writing a separate method for each type of meat. The only thing that varies is the cooking time. Burgers take 15 minutes, chicken takes 20 minutes, and steak takes 10 minutes.
We want to use the same general process for all of these types of meat. The details will vary.
You may think, “Oh, this is a great time for a function!” But it is a little more complicated than that. As we said above, we are trying to use the concept of execution context to show our cooking skills. You wouldn’t want to cook burgers, chicken, and steak for the first time for an entire party. So, we must represent the skills you have gained over years of cooking, and how you will be applying them to this one particular scenario.
In this case, our grill method just logs a sentence about when the individual person’s food will be ready. We are going to use bind() to store an execution context. To be clear, the execution context will have two important details.
- A reference to the cookout object to make sure we use the correct object
- The number of minutes of cooking
This represents our existing knowledge about how to cook the different types of meat. In each case, we are storing the object and the number of minutes, so we can quickly handle the requests from all the party attendees.
Each variable — cookBurger, cookChicken, and cookSteak — is a new function that can be executed at any time with one more argument: the person’s name. So here are three people and their food requests:
- Jack wants a burger
- Jill wants steak
- David wants chicken
By using our new functions, we can quickly take these requests without rewriting the grill method. Each of the examples below takes the final argument that is needed for the function to execute in the context of the cookout object.
Imagine if you were not able to use the bind method here! It would be kind of like you were cooking burgers, chicken, and steak for the first time when the party started. You would be feeding in three arguments to a general grill() method, with no previous planning.
Instead, we use partial function application to show that we know how to cook each type of meat. We just need to hear what each individual guest wants to eat. This split represents your actual cooking experience.
An Introduction To The Call Method
Here’s another scenario. Let’s say that when you and your partner cook a fancy dinner, you usually like to make some sort of fish and wine. As you can see from the first code snippet, you usually like to cook the fish in the oven.
But, you decide that one night, you would like to make steak instead. You are going to need to use the grill to make that steak, obviously.
Here’s the issue: your grill() method is within the context of the cookout object! But now, you want to use those cooking skills within the fancyDinner object. Remember, you don’t want to rewrite the grill method — that will make your code harder to maintain.
So, our default drink for cookouts is soda, and the default drink for fancy dinners is wine. Now, we just need to add the unusual part as an argument in the call() method — “steak.” Here is the difference between using the method normally, and using call().
The first example should be pretty straightforward: it is all in the context of the cookout object. But in the second example, the first argument changed the context of this to the fancyDinner object!
When you get to the console.log statement within the grill() method, you can see that it references a single argument, meal, as well as this.drink.
When you use fancyDinner as the first argument of the call method, that sets the context to the fancyDinner object. Now, you are able to use those grilling skills in another context.
An Introduction To the Apply Method
The apply() method is very similar to call(), except for one important difference. It can accept an array of arguments, instead of declaring individual parameters. That means that you can create a variadic function — that is, a function with any number of arguments. For that reason, it can only accept two parameters: the context, and an array of arguments.
Let’s return to our original birthday party example. You are holding a cookout for your son or daughter’s 10th birthday party. 12 kids replied and said they were going, but you do not know how many will actually show up. So, you need to be prepared to grill for an unknown number of people.
However, unlike bind(), functions that are called with apply() will be invoked immediately.
So, we need to create a function that can handle an array of an unknown number of meal orders, and return the full list of food that you will need to put on the grill. We can retain the organizational structure of the array, which helps give us the order that the requests came in.
To convert it to an actual array, we must use the slice() method from the array prototype. This is another handy application of the call() method, since the slice() method is not native to objects.
Finally, we must invoke the function using apply() in order to access the array in the mealOrders property. Here is how to do that.
We still must use cookout as the first argument, because just like call(), we must declare the execution context. Then, we can feed in the array from the mealOrders property.
This allows us to use an indefinite number of elements within the grill() method since we can pass in an array as the second argument.
Get The Latest Tutorials
Did you enjoy this tutorial? Give it a clap so others can find it too. Or, sign up to get my latest visualized tutorials from the CodeAnalogies blog here: