Understand call, bind and apply methods in JavaScript Understand call, bind and apply methods in JavaScript

Page content

This is frequently asked question in JavaScript interview. This post describes the Function prototype methods call(), apply() and bind() with their syntax, usage and practical examples.

What is Function.prototype?


First of all we need to understand that all these three functions call, apply and bind are prototype of Function. What does that mean?

Let’s print Function’s structure to understand it.

console.dir(Function);
▼ ƒ Function()
      arguments: (...)
      caller: (...)
      length: 1
      name: "Function"
    ▼ prototype: ƒ ()
          arguments: (...)
          caller: (...)
          length: 0
          name: ""
        ➤ constructor: ƒ Function()
        ➤ apply: ƒ apply()
        ➤ bind: ƒ bind()
        ➤ call: ƒ call()
        ➤ toString: ƒ toString()
        ➤ Symbol(Symbol.hasInstance): ƒ [Symbol.hasInstance]()
        ➤ get arguments: ƒ ()
        ➤ set arguments: ƒ ()
        ➤ get caller: ƒ ()
        ➤ set caller: ƒ ()

You can see apply, bind and call are prototype functions of Function, that means you can use these three functions on any new function you define.

When to use call(), bind() and apply()


When you define a new function and call it from somewhere in your code, it is executed in a context. This context is called this and it refers to an object. This object can vary depending upon how you called a function.

var a = 1;
var increment = function(){
    return console.log(this.a + 1);  
}

var obj1 = { a: 2, increment: increment };
var obj2 = { a: 3, increment: increment };

increment();       //this = window
obj1.increment();  //this = obj1
obj2.increment();  //this = obj2
Output
2 3 4

We can see in above code snippet that we called same increment() function in three different context which changes the this reference and resulting into three different output.

Here,

  • first increment() function is called in global context where this binds to global object. window is the global object if you are working in browser.
  • next two increment() functions are called in implicit context where this binds to the object on which function is called. This is called implicit context because function is tightly coupled with object (defined inside object) and this reference binds to object at compile time when you call a function from object like obj.ƒn()

Sometime we may want to call a function in explicit context where we can control on this reference at runtime when calling a function. This is where call(), apply() and bind() comes into play. Let’s call the same increment() function using call(), apply() and bind() methods.

var increment = function(){
    return console.log(this.a + 1);  
}

var obj1 = { a: 4 };
var obj2 = { a: 5 };

increment.call(obj1);               //this = obj1
increment.call(obj2);               //this = obj2

increment.apply(obj1);              //this = obj1
increment.apply(obj2);              //this = obj2

var bindFn1 = increment.bind(obj1);
var bindFn2 = increment.bind(obj2);
bindFn1();                           //this = obj1
bindFn2();                           //this = obj2
Output
5 6 5 6 5 6

Few points to note from above code snippet:-

  1. All these three call(), apply() and bind() are prototype of Function so you are able to use them on any function increment.call(), increment.apply() and increment.bind().
  2. All these three call(), apply() and bind() providing different object context (obj1, obj2) at runtime and resulting into different output.
  3. Where call() and apply() execute a function immediately, bind() returns a bound function which can be executed later.

Now that we have understood the basic usage of these prototype functions, let’s look at their syntax and practical usage with example.

call()


Syntax

functionName.call(thisArg, arg1, arg2, ...) 

When a function is called using call(),

  1. this refers to thisArg
  2. Comma separated arguments arg1, arg2 ... are the arguments of function

Remember: “call() arguments are separated by commas”.

Practical Usage

1. Borrow functionality of other objects

In Javascript, every object can have prototype functions which are actually meant to be executed only on that object. Using call() methods, you can borrow the functionality of these objects and execute it on different object.

Let’s look at the Array prototype functions

console.dir(Array);
▼ ƒ Array()
      arguments: (...)
      caller: (...)
      length: 1
      name: "Array"
    ▼ prototype: ƒ ()
          length: 0
        ➤ constructor: ƒ Array()
        ➤ concat: ƒ concat()
        ➤ copyWithin: ƒ copyWithin()
        ➤ fill: ƒ fill()
        ➤ find: ƒ find()
        ➤ findIndex: ƒ findIndex()
        ➤ lastIndexOf: ƒ lastIndexOf()
        ➤ pop: ƒ pop()
        ➤ push: ƒ push()
        ➤ reverse: ƒ reverse()
        ➤ shift: ƒ shift()
        ➤ unshift: ƒ unshift()
        ➤ slice: ƒ slice()
        ➤ sort: ƒ sort()
        ➤ splice: ƒ splice()
        ➤ includes: ƒ includes()
        ➤ indexOf: ƒ indexOf()
        ➤ join: ƒ join()
        ➤ keys: ƒ keys()
        ➤ entries: ƒ entries()
        ➤ values: ƒ values()
        ➤ forEach: ƒ forEach()
        ➤ filter: ƒ filter()
        ➤ flat: ƒ flat()
        ➤ flatMap: ƒ flatMap()
        ➤ map: ƒ map()
        ➤ every: ƒ every()
        ➤ some: ƒ some()
        ➤ reduce: ƒ reduce()
        ➤ reduceRight: ƒ reduceRight()
        ➤ toLocaleString: ƒ toLocaleString()
        ➤ toString: ƒ toString()

Let’s borrow the functionality of Array prototype methods using call() function:-

Array.prototype.concat.call([1,2,3], [4,5]);
Array.prototype.join.call([1,2,3,4,5], ":")
Output
[1, 2, 3, 4, 5] 1:2:3:4:5

Similarly borrow the start functionality of car object to use it for aircraft

var car = {
    name: 'car',
    start: function() { 
        console.log('Start the ' + this.name);
    },
    speedup: function() {
        console.log('Speed up the ' + this.name)
    },
    stop: function() {
        console.log('Stop the ' + this.name);
    }
};

var aircraft = {
    name: 'aircraft',
    fly: function(){
        console.log('Fly');
    }
};

car.start.call(aircraft);
Output
Start the aircraft
2. Chaining constructors
function Box(height, width) {
    this.height = height;
    this.width  = width;
}

function Widget(height, width, color) {
    Box.call(this, height, width);
    this.color = color;
}

function Dialog(height, width, color, title) {
    Widget.call(this, height, width, color);
    this.title = title;
}

var dialog = new Dialog('red', 100, 200, 'Title');

We can see in above code snippet how we can chain the constructors by calling parent constructor function in current this context

apply()


Syntax

functionName.apply(thisArg, [arg1, arg2, ...]) 

When a function is called using apply() method,

  1. this refers to thisArg
  2. second argument is an array of values [arg1, arg2, ...], are the arguments of function

Remember: “apply() accepts arguments as an Array”

Practical Usage

Practical usage of apply() function is same as call() function. The only difference between them, apply() accepts args as an array whereas call() accepts args as comma separated values.

We can pass arguments as an array using apply() function to call any function which accepts args as comma separated values.

Math.min(1, 2, 3, 4, 5);           //args as comma separated
Math.min.apply([1, 2, 3, 4, 5]);   //args as an array

bind()


Syntax

functionName.bind(thisArg)

When a function is called using bind() method,

  1. this refers to thisArg
  2. returns new bound function which can be called later

Remember: “bind() doesn’t call the function immediately. It returns a new bound function which can be called later.

Practical Usage

1. Bounded context
var Button = function(content) { 
  this.content = content;
};
Button.prototype.click = function() {
  console.log(this.content + ' clicked');
};

var myButton = new Button('OK');
myButton.click();

var looseClick = myButton.click;
looseClick(); // not bound, 'this' is not myButton - it is the global object

var boundClick = myButton.click.bind(myButton);
boundClick(); // bound, 'this' is myButton
Output
OK clicked undefined clicked OK clicked
2. Binding functions with parameters
var logProp = function(prop) {
    console.log(this[prop]);
};

var Obj = {
    x : 5,
    y : 10
};

Obj.log = logProp.bind(Obj);
Obj.logX = logProp.bind(Obj, 'x'); //binding with prop x
Obj.logY = logProp.bind(Obj, 'y'); //binding with prop y

Obj.log('x');
Obj.logX();

Obj.log('y');
Obj.logY();
Output
5 5 10 10

Another example,

var sum = function(a, b) {
  return a + b;
};

var add5 = sum.bind(null, 5);
//add5 is binding function with a = 5
console.log(add5(10)); //b =10
Output
15

Call vs Bind vs Apply


Comparison between function objects, function calls, call, apply and bind:

time of function execution time of this binding
function object ƒ future future
function call ƒ() now now
ƒ.call() now now
ƒ.apply() now now
ƒ.bind() future now

Let’s look at last example of this post and use call(), apply() and bind() methods all together

let numObj1 = {num: 1};
let numObj2 = {num: 2};

let sumFn = function(...args){
      console.log(this.num + args.reduce((a,b)=> a+b, 0));
}

sumFn.call(numObj1, 1, 2, 3, 4); //this = numObj1
sumFn.call(numObj2, 1, 2, 3, 4); //this = numObj2

sumFn.apply(numObj1, [1,2,3,4]); //this = numObj1
sumFn.apply(numObj2, [1,2,3,4]); //this = numObj2

let sumBindFn1 = sumFn.bind(numObj1); // return Fn
let sumBindFn2 = sumFn.bind(numObj2); // return Fn
sumBindFn1(1, 2, 3, 4);          //this = numObj1
sumBindFn2(1, 2, 3, 4);          //this = numObj2
output:
11
12
11
12
11
12