In this article, I am going to explain how to use call, apply, and bind in JavaScript with simple examples.
We will also implement an example that showcases how you can create your own map function with the apply function.
Without further ado, let's get started.
Table of Contents
Prerequisites
Here are some of the things you should understand to get the most out of this article:
Definitions
Let's look at the functions we'll be studying here a bit more closely to understand what they do.
Call is a function that helps you change the context of the invoking function. In layperson's terms, it helps you replace the value of this inside a function with whatever value you want.
Apply is very similar to the call function. The only difference is that in apply you can pass an array as an argument list.
Bind is a function that helps you create another function that you can execute later with the new context of this that is provided.
Now we will look at some basic examples of the call, apply, and bind functions. Then we will look at an example were we will be constructing our own function similar to the map function.
How to Use the Call Function in JavaScript
call is a function that you use to change the value of this inside a function and execute it with the arguments provided.
Here is the syntax of the call function:
func.call(thisObj, args1, args2, ...)
Where,
func is a function that needs to be invoked with a different
thisobjectthisObj is an object or a value that needs to be replaced with the
thiskeyword present inside the functionfuncargs1, args2 are arguments that are passed to the invoking function with the changed
thisobject.
Note that if you invoke a function without any thisObj argument, then JavaScript considers this property to be a global object.
Now that we have some context around what the call function is, let's start off by understanding it in more detail with some examples.
How to call a function with different contexts in JS
Consider the below example. It consists of 3 classes – Car, Brand1, and Brand2.
function Car(type, fuelType){
this.type = type;
this.fuelType = fuelType;
}
function setBrand(brand){
Car.call(this, "convertible", "petrol");
this.brand = brand;
console.log(`Car details = `, this);
}
function definePrice(price){
Car.call(this, "convertible", "diesel");
this.price = price;
console.log(`Car details = `, this);
}
const newBrand = new setBrand('Brand1');
const newCarPrice = new definePrice(100000);
If you look carefully, you can see that we use the call function to invoke the Car function on two occasions. Firstly, in the setBrand and then in the definePrice functions.
In both of these functions, we invoke the Car function with this object representing to the respective functions themselves. For example, inside setBrand, we call the Car function with the this object belonging to its context. The case is similar for definePrice.
Here’s a short video illustrating this: https://www.canva.com/design/DAFD4b369JM/watch
How to call a function with no arguments in JS
Consider the below example:
const newEntity = (obj) => console.log(obj);
function mountEntity(){
this.entity = newEntity;
console.log(`Entity ${this.entity} is mounted on ${this}`);
}
mountEntity.call();
In this example, we invoked the function mountEntity with no thisObj argument. In such cases, JavaScript refers to the global object.
How to Use the Apply Function in JavaScript
The Apply function is very similar to the Call function. The only difference between call and apply is the difference in how arguments are passed.
In apply, arguments you can pass an argument as an array literal or a new array object.
Here is the syntax for the apply function:
func.apply(thisObj, argumentsArray);
Where,
func is a function that needs to be invoked with a different
thisobjectthisObj is an object or a value that needs to be replaced with the
thiskeyword present inside the functionfuncargumentsArray can be an array of arguments, array object, or the arguments keyword itself.
As you can see above, the apply function has different types of syntaxes.
The first syntax is a simple one. You can pass in an array of arguments like below:
func.apply(thisObj, [args1, args2, ...]);
The second syntax is where we can pass in the new array object to it:
func.apply(thisObj, new Array(args1, args2));
The third syntax is where we can pass in the arguments keyword:
func.apply(thisObj, arguments);
arguments is a special object available inside a function. It contains values of the arguments that are passed to a function. You can use this keyword with the apply function to take any number of arbitrary arguments.
The best part about apply is we don’t need to take care of the number of arguments that are passed to the invoking function. Because of its dynamic and versatile nature, you can use it in complicated situations.
Let’s look at the same example as above, but this time we'll use the apply function.
function Car(type, fuelType){
this.type = type;
this.fuelType = fuelType;
}
function setBrand(brand){
Car.apply(this, ["convertible", "petrol"]); //Syntax with array literal
this.brand = brand;
console.log(`Car details = `, this);
}
function definePrice(price){
Car.apply(this, new Array("convertible", "diesel")); //Syntax with array object construction
this.price = price;
console.log(`Car details = `, this);
}
const newBrand = new setBrand('Brand1');
const newCarPrice = new definePrice(100000);
And here is an example that showcases how you'd use the arguments keyword:
function addUp(){
//Using arguments to capture the arbitrary number of inputs
const args = Array.from(arguments);
this.x = args.reduce((prev, curr) => prev + curr, 0);
console.log("this.x = ", this.x);
}
function driverFunc(){
const obj = {
inps: [1,2,3,4,5,6]
}
addUp.apply(obj, obj.inps);
}
driverFunc();
How to Use the Bind Function in JavaScript
The bind function creates a copy of a function with a new value to the this present inside the calling function.
Here is the syntax for the bind function:
func.bind(thisObj, arg1, arg2, ..., argN);
Where,
func is a function that needs to be invoked with a different
thisobjectthisObj is an object or a value that needs to be replaced with the
thiskeyword present inside the functionfuncarg1, arg2…argN – you can pass 1 argument to the calling function or more than that, similar to the
callfunction.
The bind function then returns a new function that consists of a new context to the this variable present inside the calling function:
func(arg1, arg2);
Now this function func can be executed later with the arguments.
Let's look at a classic example of how to use a bind function with the help of a class-based React component:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 1
};
}
handleCode() {
console.log("HANDLE CODE THIS = ", this.state);
}
render() {
return <button onClick={this.handleCode}>Click Me</button>;
}
}
Consider the above App component. It constitutes the following things:
constructoris a function that gets called a class and is instantiated with anewkeyword.renderis a function that executes/renders the JSX code.handleCodeis a class method that logs the state of the component.
If we click on the Click Me button then we will receive an error stating: Cannot read properties of undefined (reading 'state').
Have you ever wondered why this issue occurs? 🤔🤔
You might be expecting that we should be able to access the state of the class since handleCode is a class method. But here is the catch:
thisinside thehandleCodeis not same as that of the class’sthis.Inside a class,
thisis a regular object that has non-static class methods as its properties. Butthisinside thehandleCodewill refer to a different context.To be honest, the value of
thisin this scenario depends on from where the functions is being called. If you see, thehandleCodeis being called ononClickevent.But at this stage, we will get
undefinedfor the context ofthispresent inside thehandleCodefunction.We're trying to call the
stateproperty of an undefined value. Therefore, this leads to the above error.
We can fix this by providing the right context of this inside the handleCode method. You can do this with the bind method.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 1
};
this.handleCode = this.handleCode.bind(this); //bind this function
}
handleCode() {
console.log("HANDLE CODE THIS = ", this.state);
}
render() {
return <button onClick={this.handleCode}>Click Me</button>;
}
}
The bind will create a new function and store it inside the this object with a new property as handleCode. Bind will make sure that the class’s this context gets applied to the this present inside the handleCode function.
How to Create Your Own map Function
Now that we have all the necessary things, let's start off by creating our own map function. Let's first understand the things that we will need to build our own map function.
Here is the syntax of the map function:
arr.map(func)
Where,
arr is an array on which the map is called.
func is the function that needs to run on each element of an array.
The basic functionality of a map function is simple:
It is a function that walks through each element of an array and applies the function that is passed as an argument. The return type of a map is again an array with func being applied on each element.
Now we understand the requirements, so we can move on to create our own map function. Here is the code of our new map function:
function newMap(func){
let destArr = [];
const srcArrLen = this.length;
for(let i = 0; i < srcArrLen; i++){
destArr.push(func.call(this, this[i]));
}
return destArr;
}
Let's understand the above function bit-by-bit:
This function accepts an argument called
func. It's nothing but a function that needs to be called on each element of an array.The other parts of the code are pretty self explanatory. We will focus on the following line:
destArr.push(func.call(this, this[i]));This line does two things:
Pushes the changes into the
destArrExecutes the
funcwith the help ofcallmethod. Here thecallmethod (as explained in the previous sections) will execute thefuncmethod with a new value to thethisobject present inside thefuncmethod.
Now let's take a look at how we are going to execute our newMap function. The below approach of adding a new method to the existing primitive data type is not recommended but still we will do it for the sake of this article.
NOTE: do not follow the below approach in your production code. This can cause damage to the existing code.
Object.defineProperty(Array.prototype, 'newMap', {
value: newMap
});
defineProperty we create a new property inside the Array.prototype.
Once this is done, we are good to go with executing our new map function on an array.
const arr = [1,2,3];
const newArr = arr.newMap(item => item + 1);
console.log(newArr);
Summary
This article showed you what the call, apply, and bind functions can do via examples.
So to talk about these functions in brief:
Call, apply, and bind are the functions that help you change the context of the
thiskeyword present inside the invoking function.We saw how each function can be called in different ways – for example, with
applyyou can execute a function with an array of arguments, and with thecallfunction you can execute the same but the arguments are spread via commas.These functions are really useful in class-based components of React.
Thank you for reading!