Understanding call(), apply() and bind() in JavaScript

devcj_in

CJ

Posted on January 1, 2021

Understanding call(), apply() and bind() in JavaScript

In this tutorial, we will understand call, bind, and apply methods in JavaScript.

To understand these methods you should know "this" keyword in JavaScript, if you don't, read my article to understand about "this" keyword here:

Let's begin with why we need to know "this" keyword beforehand to understand call, bind, and apply methods

So now you must know that

  • Every function gets this property automatically
  • The context of this is the function it is being called from, i.e., before the dot. For e.g.,
'use strict'

let userA = {
 age: 9,
 getAge() {
  console.log(this.age)
 }
}
// here context of getAge is object userA
userA.getAge() // 9

// or

let userB = {
 age: 19
}

function getAge() {
  console.log(this.age)
}
// here context of getAge is object userB
userB.getAge = getAge

userB.getAge() // 19
Enter fullscreen mode Exit fullscreen mode

But sometimes we lose our reference this
An example:

'use strict'

let car = {
  manufacturer: 'Mahindra',
  model: 'XUV500',
  featureArray: ['Sunroof', 'ABS', '4WD'],

  carInfo() {
    const info = `${this.manufacturer} ${this.model} have these features: `

    const features = this.featureArray.reduce(function (string, feature, i) {
      if (i === this.featureArray.length - 1) {
        return `${string} and ${feature}.`
      }
      return `${string} ${feature},`
    }, '')

    console.log(info + features)
  },
}

car.carInfo()

Enter fullscreen mode Exit fullscreen mode

This will throw a TypeError: Cannot read property 'featureArray' of undefined

Which happens when code tries to access .featureArray of this which is on line 12

 if (i === this.featureArray.length - 1) {}
Enter fullscreen mode Exit fullscreen mode

According to error this.featureArray is undefined.

Let's see why this happened:

'use strict'

let car = {
  manufacturer: 'Mahindra',
  model: 'XUV500',
  featureArray: ['Sunroof', 'ABS', '4WD'],

  carInfo() {
    const info = `${this.manufacturer} ${this.model} have these features: `

    //                πŸ‘‡here this is referenced to car object
    const features = this.featureArray.reduce(function (string, feature, i) {
      console.log(this) // undefined
      //        πŸ‘‡here reference of this is unknown
      if (i === this.featureArray.length - 1) {
        return `${string} and ${feature}.`
      }
      return `${string} ${feature},`
    }, '')

    console.log(info + features)
  },
}

car.carInfo()
Enter fullscreen mode Exit fullscreen mode

The reference of this is unknown because the anonymous function we pass to .reduce does not get the context of user.

Let's solve this problem with a hack first, by saving the reference of this:

'use strict'

let car = {
  manufacturer: 'Mahindra',
  model: 'XUV500',
  featureArray: ['Sunroof', 'ABS', '4WD'],

  carInfo() {
    const info = `${this.manufacturer} ${this.model} have these features: `

    let savedReference = this

    const features = this.featureArray.reduce(function (string, feature, i) {
      if (i === savedReference.featureArray.length - 1) {
        return `${string} and ${feature}.`
      }
      return `${string} ${feature},`
    }, '')

    console.log(info + features)
  },
}

car.carInfo() // Mahindra XUV500 have these features:  Sunroof, ABS, and 4WD.
Enter fullscreen mode Exit fullscreen mode

We will fix this by using bind() later in this article, before that

Let's learn about bind() method available in JavaScript

The bind() method creates a new function that, when called, has this keyword set to the provided value.

'use strict'

let kid = {
  Name: 'Rob',
  Age: 6,
}
function sayHi() {
  console.log('πŸ‘‹ Hello, I am ' + this.Name)
}

sayHi()
Enter fullscreen mode Exit fullscreen mode

Here it will throw an error : TypeError: Cannot read property 'Name' of undefined
Because of sayHi() being called without any context, this has not referenced to anything here.

So let's fixed the context of this to sayHi() with bind()

'use strict'

let kid = {
  Name: 'Rob',
  Age: 6,
}
function sayHi() {
  console.log('πŸ‘‹ Hello, I am ' + this.Name)
}

let logHi = sayHi.bind(kid) // creates new object and binds kid. 'this' of sayHi = kid now

logHi() // πŸ‘‹ Hello, I am Rob

Enter fullscreen mode Exit fullscreen mode

So now we understand how bind works, let's solve the car problem with bind instead of the previous hack above

'use strict'

let car = {
  manufacturer: 'Mahindra',
  model: 'XUV500',
  featureArray: ['Sunroof', 'ABS', '4WD'],

  carInfo() {
    const info = `${this.manufacturer} ${this.model} have these features: `

    const features = this.featureArray.reduce(
      function (string, feature, i) {
        if (i === this.featureArray.length - 1) {
          return `${string} and ${feature}.`
        }
        return `${string} ${feature},`

        // πŸ‘‡here we have bind the this object which is referenced to object car
      }.bind(this),
      ''
    )

    console.log(info + features)
  },
}

car.carInfo() //Mahindra XUV500 have these features:  Sunroof, ABS, and 4WD.
Enter fullscreen mode Exit fullscreen mode

Now we have covered bind(), let's understand call() and apply()

What is a call method in JavaScript?

Every function has a method call that allows invoking the function specifying the context (this) and arguments the function will be invoked with

'use strict'

let kid = {
  Name: 'Rob',
  Age: 6,
}

function sayHi() {
  console.log('πŸ‘‹ Hello, I am ' + this.Name)
}

sayHi.call(kid) // πŸ‘‹ Hello, I am Rob
Enter fullscreen mode Exit fullscreen mode

We can pass arguments to it also :

'use strict'

let kid = {
  Name: 'Rob',
  Age: 6,
}

function sayHi(place, number) {
  console.log(`πŸ‘‹ Hello, I am ${this.Name}, I live in ${place} and I have ${number} dogs`)
}

sayHi.call(kid, 'Montreal', 2) // πŸ‘‹ Hello, I am Rob, I live in Montreal and I have 2 dogs
Enter fullscreen mode Exit fullscreen mode

Let's see how apply() works :

call() and apply() are exact same thing. The only difference between how they work is that call() expects all arguments to be passed in individually, whereas apply() expects an array of all of our arguments.

'use strict'

let kid = {
  Name: 'Rob',
  Age: 6,
}

function sayHi(place, number) {
  console.log(`πŸ‘‹ Hello, I am ${this.Name}, I live in ${place} and I have ${number} dogs`)
}

sayHi.apply(kid, ['Montreal', 2]) // πŸ‘‹ Hello, I am Rob, I live in Montreal and I have 2 dogs
Enter fullscreen mode Exit fullscreen mode

I hope this article was helpful for you to understand call(), bind(), and apply().

Feel free to give a suggestion or shoot me a message on Twitter or LinkedIn and while you are at it checkout my Website.
See you in the next post!πŸ‘‹

πŸ’– πŸ’ͺ πŸ™… 🚩
devcj_in
CJ

Posted on January 1, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related