Understanding call(), apply() and bind() in JavaScript
Tags: javascript, webdev, codenewbie, node
Published: 2021-01-01 22:25:31

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: {% link https://dev.to/devcj_in/the-this-keyword-in-javascript-2mdp %}

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.,
```javascript
'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
```

But sometimes we lose our reference `this`
An example: 
```javascript
'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()

```
## 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

```javascript
 if (i === this.featureArray.length - 1) {}
```

According to error `this.featureArray` is undefined.

Let's see why this happened:

```javascript
'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()
```
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`:
```javascript
'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.
```
### 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.

```javascript
'use strict'

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

sayHi()
```

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()`

```javascript
'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

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

```javascript
'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.
```

### 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


```javascript
'use strict'

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

function sayHi() {
  console.log('👋 Hello, I am ' + this.Name)
}

sayHi.call(kid) // 👋 Hello, I am Rob
```
We can pass arguments to it also :

```javascript
'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
```
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.

```javascript
'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
```

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](https://twitter.com/stoic_cj) or [LinkedIn](https://www.linkedin.com/in/chamanjatoliya) and while you are at it checkout my [Website](https://devcj.in).
See you in the next post!👋