DEV Community

loading...

Javascript call and apply 101

Igor Irianto
Vim, Rails, cheesy puns
Originally published at irian.to Updated on ・3 min read

If you spent enough time reading Javascript codes, you probably saw call and apply. If you are like me, you get confused real fast. Don't worry, these methods are pretty easy to understand. I will cover some of the basics to get you all started!

I will go over:

  1. How to use call
  2. How to use apply
  3. When to use call and when to use apply

Before we start, keep in mind that these two are very similar. Learning one allows us to understand the other.

Using call

Suppose we have an object and a function:

const breakfastObj = {
  food: 'blueberry waffles',
  drink: 'orange juice'
}

function sayBreakfast(){
  console.log(`I had ${this.food} and ${this.drink} for breakfast`)
}
Enter fullscreen mode Exit fullscreen mode

When we call sayBreakfast(), it will return

sayBreakfast() // I had undefined and undefined for breakfast
Enter fullscreen mode Exit fullscreen mode

To "call" the function sayBreakfast() with breakfastObj as this:

sayBreakfast.call(breakfastObj) // I had blueberry waffles and orange juice for breakfast
Enter fullscreen mode Exit fullscreen mode

Recall that this, if not defined, refers to global object (if you are on browser, your global object is probably window object). So we can do this:

window.food = 'French toast'
window.drink = 'Apple juice'
sayBreakfast() // ... French toast and Apple juice
Enter fullscreen mode Exit fullscreen mode

This is equivalent to:

sayBreakfast.call() // ... French toast and Apple juice
Enter fullscreen mode Exit fullscreen mode

Call also accepts 2nd, 3rd, ...nth arguments. These arguments are used as function's arguments. Let's look at example to clarify:

const lunchObj = {
  food: 'tacos',
  drink: 'water'
}

function sayLunch(location){
  console.log(`I had ${this.food} and ${this.drink} for lunch at ${location}`)
}

sayLunch.call(lunchObj, "Taco Bell") // I had tacos and water for lunch at Taco Bell
Enter fullscreen mode Exit fullscreen mode

Hmm, tacos sound good 🤤. If the function accepts multiple arguments, we can pass them too:

function sayLunch(location, company, time){
  console.log(`I had ${this.food} and ${this.drink} for lunch at ${location} with ${company} in the ${time}`)
}

sayLunch.call(lunchObj, "Taco Bell", "Jon and Juan", "afternoon") // I had tacos and water for lunch at Taco Bell with Jon and Juan in the afternoon
Enter fullscreen mode Exit fullscreen mode

Using apply

apply works like call. The only difference is the way they accept function arguments. apply uses array instead of separated by comma: myFunction.apply(obj, [arg1, arg2, argn])

Using our example earlier, but with apply:

const lunchObj = {
  food: 'tacos',
  drink: 'water'
}

function sayLunch(location, company, time){
  console.log(`I had ${this.food} and ${this.drink} for lunch at ${location} with ${company} in the ${time}`)
}

sayLunch.apply(lunchObj, ["Taco Bell", "Jon and Juan", "afternoon"])
Enter fullscreen mode Exit fullscreen mode

We can take advantage of apply's array arguments with ES6's spread operator

Here is a shameless copy-paste from mozilla page:

function sum(x, y, z) {
  return x + y + z;
}

const numbers = [1, 2, 3];

console.log(sum(...numbers));
// expected output: 6

console.log(sum.apply(null, numbers));
// expected output: 6
Enter fullscreen mode Exit fullscreen mode

Keep in mind we can use call and/or apply into built-in functions, not custom functions. Something like this:

const someArr = ["foo", "bar", "baz"];

console.log.apply(null, someArr) // foo bar baz
Enter fullscreen mode Exit fullscreen mode

And if we want to get fancy and append a new argument into someArr:

console.log.apply(null, ['hello', ...someArr]) // hello foo bar baz
Enter fullscreen mode Exit fullscreen mode

How to remember call vs apply arguments

A trick to remember which one is which is to look at their first letter (credit SO)

  • A -> Apply -> Array
  • C -> Comma -> Call

We only scratched the surface, but hopefully this should be enough to apply (pun intended 😎) your knowledge for more advanced stuff!

Resources/ more readings:

Discussion (6)

Collapse
jannikwempe profile image
Jannik Wempe

Love the simplicity and especially the trick about how to remember the difference.

But I am missing the part "When to use call and when to use apply", which I was most interested in ;-) Is it just about preference?

Collapse
jakewhelan profile image
Jake Whelan • Edited

It just depends on the structure of your arguments.

call is useful for manually calling a function where you have explicitly defined arguments.

const a = 'bar'
const b = 'baz'

foo.call({}, a, b)

But sometimes you have an array of arguments and you want to programmatically call a function with them, that's where apply is useful.

const args = ['bar', 'baz']

foo.apply({}, args)

That said, with the advent of the spread/rest operator (...), apply is redundant.

const args = ['bar', 'baz']

// notice this is using call, not apply!
foo.call({}, ...args)
Collapse
iggredible profile image
Igor Irianto Author • Edited

Hey Jannik! I'm glad you liked it.
As for the usage, it is mostly up to your preference. In addition to what Jake covered (for arguments), you can use call and apply to give this inside a function a different object.

const someObj = {
  name: 'iggy',
  sayName() {
     console.log(`The name is ${this.name}`)
  }
}

Normally I'd do someObj.sayName(). But if one day I go back to reuse sayName, but I want to pass it a different name, you can just use call/apply. someObj.sayName.call({name: 'Russell'}).

It's like inheriting and reusing other object's properties.

Collapse
kristoftombacz profile image
kristoftombacz

i read a lot of article about call and apply, but still didnt get the idea WHY to use it. why should i introduce these methods to the project (which might be unknown for some), when i can write less, but a more straightforward code?

your example for call is the following

const breakfastObj = {
  food: 'blueberry waffles',
  drink: 'orange juice'
}

function sayBreakfast(){
  console.log(`I had ${this.food} and ${this.drink} for breakfast`)
}

sayBreakfast.call(breakfastObj)

instead of this, why shouldnt i just write

function sayBreakfast({ food, drink }){
  console.log(`I had ${food} and ${drink} for breakfast`)
}

sayBreakfast(breakfastObj)

am i missing something?

Collapse
iggredible profile image
Igor Irianto Author • Edited

Hey kristoftombacz , thanks for replying!

That's a good question, thanks for bringing it up! I used that example because I think that is easiest to understand/ to make a point. I aimed this article for simplicity, I apologize for not being the most practical.

As for the real life application, here are two, I am sure there are myriads more:

  • Scoping

What if you want to call private function but don't want to expose it to public? We can use call. Here is an example (source):

var obj = (function() {
    var privateFn = function() {
        alert(this.id);
    }
    return {
        id: 123,
        publicFn: function() {
            privateFn.call(this);
        }
    };
}());
  • Object reuse

What if you have an object that you later decide, "hey, this is useful, I want to extend this object to use with other attributes!".

Here is another example (souce)

var Dave = {
    name : 'Dave',
    age    : '30',
    getIdNumber: function(){
        return 'id number of dave';   },
    bookCheapFlightTickets: function( place, numberOfPeople, paymentDetails ){
        function sendSecretCode(){
            // Dave's own logic for generating secret code
            // and sending to his friends
            // ...      }
        function bookTickets(){
            // book the tickets using the given details
            // the ticket will be printed always in this.name , this.age, this.getIdNumber() and here **this** is pointing to Dave
            return "Ticket is booked for "+ this.name + ", " + this.age +", "+ this.getIdNumber();
        }
        var isVerified = sendSecretCode();
        return isVerified ? bookTickets() : false;  }};

And instead of rewriting the object and going through a lot of headache (say this Dave object has been around for years - who knows where else it is being used and who knows what will break if we modify it? 😅) If I want to rewrite one, that does not sound DRY. Instead I could do something like

var Iggy = {
    name: 'Iggy',
    age:    '29',
    getIdNumber: function(){
        return 'your id number';  }}
...
var davesMethodForFlightBooking = Dave.bookCheapFlightTickets;
// using call
var bookedTicket = davesMethodForFlightBooking.call(Iggy, place, numberOfPeople, paymentDetails);

Hope this makes sense. Let me know if I can help answer any other question! 😁

Collapse
ytjchan profile image
ytjchan

I'd also avoid this as much as possible...