JS Deepdive: Call vs Apply

Today we are going to be learning about Javascripts native Call vs Apply. This is a common interview question and something that is excellent to have in your quiver.

Let's get started.

Definitions

Call The call() method calls a function with a given this value and arguments provided individually.

Apply The apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object).

Difference between call and apply

While the syntax of this function is almost identical to that of apply(), the fundamental difference is that call() accepts an argument list, while apply() accepts a single array of arguments.

Basic Example

Let's get some data to use.

var animals = {  
  'dog': {
    'name': 'benji',
    'noise': 'woof'
  },
  'cat': {
    'name': 'sheeba',
    'noise': 'meow'
  },
  'cow': {
    'name': 'benny',
    'noise': 'moo'
  },
  'sheep': {
    'name': 'fred',
    'noise': 'bah'
  }
};

Let's see what noise each animal makes.

var makeSomeNoise = function(animal){  
  console.log(animals[animal].name + ' says ' + animals[animal].noise);
};

Call

makeSomeNoise.call(animals, 'cat');  

Here we pass in the thisArg. It can be a simple this or another function/object. Then we tell it what animal we want to here the noise from.

Outputs

sheeba says meow  

It outputs that because the function looks in the animals object we created for cat. Then from there the makeSomeNoise function puts the name of the animal and the noise to create our output.

Apply

makeSomeNoise.apply(animals, ['sheep']);  

Here we pass in the thisArg. It also can be a simple this or another function. Then we give it a array of animals we want to hear from.

Outputs

fred says bah  

Another Example

function Animal(name, noise) {  
  this.name = name;
  this.noise = noise;
}

function Mammal(name, noise) {  
  Animal.call(this, name, noise);
  this.category = 'mammal';
}

function Reptile(name, noise) {  
  Animal.call(this, name, noise);
  this.category = 'reptile';
}

function Amphibian(details) {  
  Animal.apply(this,details);
  this.category = 'amphibian';
}

function Insect(details) {  
  Animal.apply(this, details);
  this.category = 'insect';
}

var alligator = new Reptile('fred', 'num num num');  
var dog = new Mammal('benji', 'roof');  
var dolphin = new Amphibian(['flipper', 'sqeak sqeak']);  
var spider = new Insect(['barney', 'pinch pinch']);

console.log(alligator);  
console.log(dog);  
console.log(dolphin);  
console.log(spider);

//These output
//Reptile {name: "fred", noise: "num num num", category: "reptile"}
//Mammal {name: "benji", noise: "roof", category: "mammal"}
//Amphibian {name: "flipper", noise: "sqeak sqeak", category: "amphibian"}
//Insect {name: "barney", noise: "pinch pinch", category: "insect"}

You may wonder what is going on in this example?

Here is the basic constructor function. They all are animals with a name and make a noise.

function Animal(name, noise) {  
  this.name = name;
  this.noise = noise;
}

Here we create a few types of Animals. We use call here so that we can borrow name and noise from the Animal Constructor. The category of each animal will be different, so we don't include it in the Animal Constructor. When done each Mammal and Reptile will have a Name , Noise and Category.

function Mammal(name, noise) {  
  Animal.call(this, name, noise);
  this.category = 'mammal';
}
function Reptile(name, noise) {  
  Animal.call(this, name, noise);
  this.category = 'reptile';
}

Like the previous example we are borrowing from Animal. In this example we are using apply to pass in a array of items. There can be any number of items in the array.

function Amphibian(details) {  
  Animal.apply(this,details);
  this.category = 'amphibian';
}
function Insect(details) {  
  Animal.apply(this, details);
  this.category = 'insect';
}

Passing in some names and noises.

var alligator = new Reptile('fred', 'num num num');  
var dog = new Mammal('benji', 'roof');  
var dolphin = new Amphibian(['flipper', 'sqeak sqeak']);  
var spider = new Insect(['barney', 'pinch pinch']);  

Printing them all out to the console.

console.log(alligator);  
console.log(dog);  
console.log(dolphin);  
console.log(spider);

//These output
//Reptile {name: "fred", noise: "num num num", category: "reptile"}
//Mammal {name: "benji", noise: "roof", category: "mammal"}
//Amphibian {name: "flipper", noise: "sqeak sqeak", category: "amphibian"}
//Insect {name: "barney", noise: "pinch pinch", category: "insect"}

Number of Arguments

Take this example.

function Animal(name, noise) {  
  this.name = name;
  this.noise = noise;
}
function Reptile(name, noise) {  
  Animal.call(this, name, noise);
  this.category = 'reptile';
}
function Amphibian(details) {  
  Animal.apply(this,details);
  this.category = 'amphibian';
}

Let's say later we updated Animal to have number of legs.

function Animal(name, noise, legs) {  
  this.name = name;
  this.noise = noise;
  this.legs = legs;
}

Then we did.

var alligator = new Reptile('fred', 'num num num', 4);  
var dolphin = new Amphibian(['flipper', 'sqeak sqeak', 0]);  
console.log(alligator);  
console.log(dolphin);

//These output
//Reptile {name: "fred", noise: "num num num", legs: undefined, category: "reptile"}
//Amphibian {name: "flipper", noise: "sqeak sqeak", legs: 0, category: "amphibian"}

Why are the legs of the reptile undefined?

They are undefined since there is no reference to legs in the Reptile function. Of course if we can update it to include legs then it will work. That is more work we have to do each time.

function Reptile(name, noise, legs) {  
  Animal.call(this, name, noise, legs);
  this.category = 'reptile';
}

Apply can take any number of arguments in. There is no reference to name , noise or legs below. Just a reference to details. Long as the passed in Array has legs they will show. In the future you will just need to updated Animal and the array you pass in and not each animal type.

function Amphibian(details) {  
  Animal.apply(this,details);
  this.category = 'amphibian';
}