DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 966,904 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Alexander Antoniades
Alexander Antoniades

Posted on • Updated on

Call, Apply, Bind - The Basic Usages

In this article I'm going to show you some of the basic usages of call/apply & bind and the problems they solve. I will also show you some practical examples in order to start using them in your projects.

Call/apply & bind are all methods within the function prototype. Both methods are doing the same thing but slightly differently. What they actually do is allows us to call a function with a given this context and arguments. They let us call a function and have access to the properties of another function or object. We can borrow methods of one object’s prototype and use it to another, for example we could apply Array’s slice method to a string or use Math’s max to find the maximum number of a bunch of numbers in an Array.

WHAT IS "THIS"?

I'm not gonna dive into much details here but understanding the this keyword is crucial to grasp the concept of call/apply & bind.

Everything in javascript is an object, functions are objects too (first-class objects). Every object(or function) has a this object assigned to it. this object acts as a reference to the object's variables and methods:

let obj = {
  name: "Alex",
  getName: function() {
    return `My Name is: ${this.name}`;
  }
}

obj.getName(); // "My Name is: Alex"
Enter fullscreen mode Exit fullscreen mode

this.name is a reference to the object's(obj) name property value. In other words this refers to the properties of this particular object.

But what if we try to access the name variable outside of the object?

let name = "Alex";
let obj = {
  getName: function() {
    return `My Name is: ${this.name}`;
  }
}

obj.getName(); // My Name is: Undefined
Enter fullscreen mode Exit fullscreen mode

Now we get undefined because name is no longer within our local variable environment.

CALL METHOD

Call accepts a this value and a list of arguments:

Function.call(this, arg1,arg2,...)
Enter fullscreen mode Exit fullscreen mode

Let's say we have an object with a food property key and a favFood function that accepts a string as an argument. In order for favFood to have access to the object's food key we need to call favFood using the call method and give it the _this context of the obj. In simple words we need to attach the obj to favFood:

let obj = { food: "Pizza" }

function favFood(text) {
  return `${this.food} ${text}`;
}

let text = "is awesome!";
favFood.call(obj, text); // "Pizza is awesome!"
Enter fullscreen mode Exit fullscreen mode

As you can see we've just passed the object (obj) as a first parameter to the call method -therefore it's context (this)- and a single argument as a second parameter to our favFood function. This way we have access to any method and property of this particular object (obj).

We could also pass multiple arguments seperated by a comma.

APPLY METHOD

Apply is the same as call but instead, apply accepts a single array of arguments.

Function.apply(this, array)
Enter fullscreen mode Exit fullscreen mode
let obj = { 
  add: function(a,b,c) {
    return a + b + c;
  }
}

function sumItUp(a,b,c) {
  return this.add(a,b,c);
}

let numbers = [1,2,3];
sumItUp.apply(obj, numbers); // 6
Enter fullscreen mode Exit fullscreen mode

BIND METHOD

Ξ€he tricky part about bind is that it has the same functionality as apply but instead of calling the function immediately, it returns a bound function:

let obj = { 
  add: function(a,b,c) {
    return a + b + c;
  }
}

function sumItUp(numbers) {
  return this.add(...numbers);
}

let numbers = [1,2,3];
let bound = sumItUp.bind(obj); // Returns a bound function
bound(numbers) // 6
Enter fullscreen mode Exit fullscreen mode

In this case we pass the obj to the sumItUp function -in order to have access to the obj context - then we call the bound function and pass an array of numbers as an argument. Nice thing about bind is that you can call the returned bound function whenever you need.

BORROWING PROTOTYPE METHODS

The cool thing about these methods (call, apply, bind) is that we can borrow methods and functionality from other object's prototype.

Borrowing Max from the Math Object

Let's say we have an array of numbers and we need to find the maximum number within the array:

let numArray = [1,3,4,5,6];
Enter fullscreen mode Exit fullscreen mode

Now we know that the Math object has a method for finding the minumum and maximum values of a list of numbers, but arrays are not supported because they're not considered as numbers therefore are not a valid parameter. If we try:

let numArray = [1,3,4,5,6];
Math.max(numArray); // NaN
Enter fullscreen mode Exit fullscreen mode

We'll get NaN (Not a Number) and that's totally normal beause an array is not a number:

Array === Number // False
Enter fullscreen mode Exit fullscreen mode

But here's where the cool part comes, by using apply we can pass our array as an argument to the Math object like this:

let numArray = [1,2,3,4,5];
Math.max.apply(null, numArray); // 5
Enter fullscreen mode Exit fullscreen mode

Here we pass null as the first argument because we don't need to pass any context to the max method, instead we only use the second argument to pass our array which is going to be converted into arguments and finally be accepted by max as a valid parameter.

Borrowing Filter from the Array Object

Now let's say we have some letters and we'd like to filter out some of them and store them in an array using the Array's filter method.

let letters = 'abcdef';
let arrayFilteredLetters = letters.filter(letter => letter);
console.log(arrayFilteredLettes); // Filter is not a function.
Enter fullscreen mode Exit fullscreen mode

We get filter is not a function because letters are a String object thus it doesn't have access to Array's filter method. What we could do is use call again to invoke filter and pass letters as an argument.

let letters = 'abcdef';
let arrayFilteredLetters = Array.prototype.filter.call(letters, letter => letter !== 'c' && letter !== 'd');
console.log(arrayFilteredLetters); // [ 'a', 'b', 'e', 'f' ]
Enter fullscreen mode Exit fullscreen mode

As you see, we can use call/apply & bind to borrow methods from one object’s prototype and use it to another. This is one of the coolest applications of call/apply & bind.

Important notes about Arrow Functions

In case of arrow functions our methods: Call/Apply & Bind doesn’t work as expected.

As the documentation of MDN states:

"Since arrow functions do not have their own this, the methods call() or apply() can only pass in parameters. thisArg is ignored."

Arrow functions doesn't have their own this. This is lexically bound and it uses the this of the context in which the arrow function was called. Call/Apply & Bind can be used only to pass parameters.

CONCLUSION

By now you should be able to understand the basic usage and applications of call/apply & bind and be able to attach different contexts to functions and objects. You’ll also be able to borrow methods from other prototypes and use it to pass unrelated values -like looping over a string using array’s filter-

In javascript there are countless ways to do something. All the examples above has many alternative syntaxes and methods to have the same results. In this article I decided to use simple examples in order to make sure you get the basic knowledge in the most simplest way.

Have Fun!

Top comments (5)

Collapse
 
sergiokag profile image
Sergio Kagiema- Fay

Nice work Alex.

A small correction upon the 'this' keyword.

A 'this' variable is created when a new execution context is created. And its value it might be anything.

Collapse
 
alexantoniades profile image
Alexander Antoniades Author • Edited on

Thank you Sergio!
Yes of course..! I don't mention how or when "this" is created. Just stating the fact that every object has a "this" assigned to it!
The value of "this" might be anything within the context that is created. Of course you can change that with the methods mentioned in this article ;)

Collapse
 
sergiokag profile image
Sergio Kagiema- Fay

Sorry, I did not make it clear.

Every object doesn't have a 'this' assigned to it.

The 'this' is related to the execution context. And the execution context means how a function is called.
That's why I mentioned that a 'this' variable is created when a new execution context is created. :)

Thread Thread
 
alexantoniades profile image
Alexander Antoniades Author

As I've already mentioned in the article....I don't go into much details about "this" and
the execution context. The purpose of this article is for a beginner to have a basic understanding of how call, apply and bind works. Saying that every object or function has it's own "this" keyword/variable/object (say it however you want) is enough to make someone understand the basics. Maybe I'll write an advanced article on these concepts next time.

thanks again for the feedback Sergio.

Collapse
 
maxsierra16 profile image
Maximo Sierra

Cool πŸ”₯

Need a better mental model for async/await?

Check out this classic DEV post on the subject.

β­οΈπŸŽ€ JavaScript Visualized: Promises & Async/Await

async await