Javascript call() & apply() vs bind()?

Javascript call() & apply() vs bind()?

  • 2016-10-07
  • 1684

Functions in JavaScript are object. As objects functions have several methods. Some of them are powerful like Bind, Call, Apply etc. In this article we will discuss about these three methods in detail. In brief, Bind is used to set this value in methods and currying functions. On the other hand Call and Apply are nearly same, used to borrow methods and set this value explicitly. This sounds a little hard. So let’s clarify them.

Bind

bind() allows us to bind specific object to this whenever a function or method is invoked i.e. it calls a function with this value set explicitly. This sounds like trivial but often this value in a function must be set explicitly when an object is required to bind to a function’s this value. Sometime this is not bound properly to the object which we expect and causes error. bind() is required whenever we use this keyword in a method and call that method from receiver object. Other uses of bind() are it allows us to borrow methods and curry function. Before we move on to the code we need to understand the this keyword in JavaScript properly. If you already understand it then it’s okay otherwise you can read our article on ‘Understanding “this” keyword in JavaScript‘.

Okay, we are now ready with this keyword. Let’s take a look an example of bind() function.

var data=[
    {"name":"John","age":32},
    {"name":"Peter","age":25}
];
 
var user={
    data:[
        {"name":"Paul","age":21},
        {"name":"Smith","age":47}
    ],
    showData:function(){
        console.log("name="+this.data[0].name+", age="+this.data[0].age);
    }
};
 
var display1=user.showData;
display1();
 
var display2=user.showData.bind(user);
display2();

In the above example display1() outputs ‘name=John, age=32′, whereas display2() outputs ‘name=Paul, age=21′. This is because when we are setting user.showData method to display1, global data variable is being accessed by this or window object is assigned and thus resulting in ‘name=John, age=32′. But in the second case we are getting ‘name=Paul, age=21′ as we have used bind() to bind specific object ‘user’ to this. That’s why it is showing data with the value set explicitly.

In JavaScript we can pass function, return them and borrow them. Next, we will see how bind() is used to borrow methods. Here is an example.

var employee={
    data:[
        {"name":"Bob","age":19},
        {"name":"Raul","age":51}
    ]
};
 
var user={
    data:[
        {"name":"Paul","age":21},
        {"name":"Smith","age":47}
    ],
    showData:function(){
        console.log("name="+this.data[0].name+", age="+this.data[0].age);
    }
};
 
var display=user.showData.bind(employee);
display();

In this example we are adding a method showData to employee object borrowed from user object using bind(). It will result in “name=Bob, age=19″, which comes from employee data. This is okay for our experiment, but the problem with this is that if employee object would have a method showData that would be overwritten. We don’t want to do this accidentally. As we will see later in this discussion ,it is better to borrow method using either Apply() or Call() method.

Next we will see how JavaScript’s Bind() is used to curry functions. Function currying is also known as partial function application, which is use of a function(with arguments) and returns a new function with some arguments already set. The returned function has also access to the stored arguments and variables of the outer function. Let’s jump into an example to clarify this.

function display(name,desig,salary){
    console.log(name+"-"+desig+"-"+salary);
}
 
display("John","Programmer",50000); //John-Programmer-50000
 
var bobData=display.bind(null,"Bob","Analyst");
bobData(60000); //Bob-Analyst-60000
 
var danielData=display.bind(null,"Daniel");
danielData("Manager",70000); //Daniel-Manager-70000
 
var ceoData=display.bind(null);
ceoData("Benn","CEO",100000);  //Benn-CEO-100000

As we can see here first display function is outputs as a normal function and rest three functions(bobData, danielData, ceoData) are curried functions. In bobData we are binding display function with name and desig argument fixed. We are modifying right most argument i.e. salary. Whenever it is being called outer property means “name:bob” and “desig:Analyst” are being accessed inside it. In danielData we are binding only the name argument and rest arguments are set as null. Similarly in ceoData we are binding no argument. They are being sent manually whenever they are being called. Thus we can use bind() for function currying.

Call and Apply

Like Bind , Call and Apply can be used to set this value when invoking functions. The first parameter in the call and apply methods set the this value to the object that the function is invoked upon. Let’s consider the following example.

function add(arr){
    this.sum=arr[0]+arr[1];
}
 
var array=[4,5];
add(array);
console.log(window.sum); //9
console.log(sum);  //same as window.sum as both points to same
 
var mySet={numbers:[10,20]};
add(mySet.numbers);
console.log(mySet.sum);  //undefined
 
add.call(mySet,mySet.numbers); 
//or
add.apply((mySet,mySet.numbers);
console.log(mySet.sum);  //30

First function ‘add(…)’ simply takes argument and set sum to window object. After creating mySet object when we try to get mySet.sum it results in undefined because this value is not set with mySet. So, after using call(…) or apply function provided with mySet object as first argument we get desired output.

The Apply and Call functions are same except Apply recieves data in array format. Another thing that Apply can do which Call can’t is executing Variable-Arity Functions.

Now we will borrow functions with Apply and Call. We can borrow functions with Apply and Call as we did with Bind method but in a little different way. Let’s see the example below

var exam={
    scores:[20,30],
    add:function(){
        this.sum=this.scores[0]+this.scores[1];
    }
};
var test={
    scores:[50,60]
};
 
exam.add.apply(test);
console.log(test.sum); //110
console.log(exam.sum); //undefined

Here test object borrows add() method from exam object and this value defined in that method will be assigned to first parameter i.e. the test object. As a result we get the sum property of test object calculated with it’s scores value and exam object’s sum property is undefined as the object is not set to the method’s this value. Another important thing is if we change the original method, the changes are reflected in the borrowed instances of that method.

We can pass an array with of arguments to a function and using the apply () method, the function will execute the items in the array if we call the function like.

someFunc(array[0],array[1],array[2],array[3],array[4]);

This is used for creating variable-arity or variadic functions. These are functions that accept any number of arguments instead of a fixed number of arguments. The arity of a function specifies the number of arguments the function is supposed to accept. A common example of variable-arity function in JavaScript is Math.max() method.

Math.max (47,21,50,95,9);
//We can pass any number of arguments here

But what if we send array of arguments to Math.max() ? The answer is we cannot do this. This is the scenario where Apply helps us to execute variadic functions. Let’s see the code.

var numbers = [47,21,50,95,9];
// We cannot pass an array of numbers to the the Math.max method 
console.log (Math.max (numbers)); // NaN
 
//But
var numbers = [47,21,50,95,9];
// Using the apply () method, we can pass the array of numbers:​
console.log (Math.max.apply (null, numbers)); // 95
 
//As Math.max() doesn't use 'this' we sent the object as null in first argument.

We will now see another example of our own variadic function to clarify the concept of using Apply here

ar fruit=["Mango","Apple","Banana","Strawberry","Grape"];
  
//As it accepts any number of arguments no parameter is defined
function getFruit(){
     var args= Array.prototype.slice.call (arguments);
     for(item in args){
         console.log(args[item]); //Mango,Apple,Banana,Strawberry,Grape
     }
 }
  
getFruit.apply(null,fruit);

Suggest

ES6 Javascript: The Complete Developer’s Guide

JavaScript Promises: Applications in ES6 and AngularJS

Accelerated ES6 JavaScript Training

ES6 Javascript Essentials (With Exercise Files)

ES6 Bootcamp - Next Generation JavaScript

Learning ECMAScript 6: Moving to the New JavaScript