# Function Borrowing methods in JavaScript and their Polyfills

Hello everyone, I'm back again with a new article. 

In this article we will discuss and learn about the **Function Borrowing methods** - `call()`, `apply()` and `bind()`. These are called function borrowing methods because the `this` context for a function can be modified using these methods. Before we jump into this discussion about what these methods do, I think it is important to understand how `this` works in JavaScript.

## `this` in JavaScript

The value of `this` in JavaScript is calculated at runtime i.e. when the code is getting executed. At a global level, `this` value equals the `window` object. Inside a normal function in an object, `this` refers to the enclosing object and inside an arrow function it references the value of `this` from the enclosing lexical context

|     place    | Value of this|
|-----|------|
|global level | equal window (global) object|
|normal functions | equal to global object | 
|normal functions inside objects | the current object |
|arrow functions|value of `this` in the enclosing lexical context|

Let's understand all these with a simple example

```JS
console.log(this); 
/**
 * window object
 */

function normalFunction() {
  console.log(this);
}
normalFunction()
/**
 * window object
 */


const arrowFunction = () => {
  console.log(this); 
};
/**
 * Does not redefine this.
 * Equal to enclosing scope in this case the window object
 */


const objectWhichContainsNormalFunction = {
  name: "tahir",
  printName: function () {
    console.log(this);
  },
};
objectWhichContainsNormalFunction.printName();
/**
 * Points to the object containing the function
 * {
 *    name: "tahir",
 *    printName: f()
 * }
 */

const objectWhichContainsArrowFunction = {
  fname: "tahir",
  lname: "ahmed",
  printName: () => {
    console.log(this);
  },
  printFullName: function () {
    (() => {
      console.log(this);
    })();
  },
};
objectWhichContainsArrowFunction.printName();
/**
 * In this case the value of this will be equal to the window object.
 *  */

 objectWhichContainsArrowFunction.printFullName();
/**
 * `this` will be equal to the object `objectWhichContainsArrowFunction`
 * This is because the `printFullName()` is a normal function and it redefines 
 * `this` value to the object
 */

```

With `this` concept being clear, let's dive into the function borrowing methods.

## `call`

`call` invokes the functions with a `this` context and arguments provided individually.

Let's understand it with an example. Consider an object person1 which is as below

```JS
const person1 = {
  fname: "john",
  lname: "doe",
  printFullName: function () {
    console.log(`${this.fname} ${this.lname}`);
  },
};
person1.printFullName()
// john doe
```
There is another object person2, which does not have the `printFullName()` implementation. We can call `printFullName()` and provide person2 as the context.

```JS
const person2= { 
  fname: "Alfred", 
  lname: "Marshall" 
}

/**
 * Invoking call method below
 */

person1.printFullName.call(person2);
// Alfred Marshall
```
It is a good practice to have these functions outside. We can pass additional arguments to the call function individually. 

```JS
const person1 = {
  fname: "Justin",
  lname: "Case",
};

const person2 = {
  fname: "Parsley",
  lname: "Montanna",
};

function greetUser(salutation, message) {
  console.log(`${salutation} ${this.fname} ${this.lname}!!! ${message}`);
}

greetUser.call(person1, "Mr", "Good Morning!");
// Mr Justin Case!!! Good Morning!

greetUser.call(person2, "Ms", "Good Morning!");
// Ms Parsley Montanna!!! Good Morning!
```
In the above example snippet, `greetUser()` function defined outside. It expects two arguments `salutation` and `message`. In `call` we pass these arguments individually.

## `apply`

`apply()` is similar to the `call()` method with the only difference being the way in which additional arguments are passed to the function while it is invoked. 

In the case of `apply()` we pass all our arguments as an array

```JS
const person1 = {
  fname: "Justin",
  lname: "Case",
};

const person2 = {
  fname: "Parsley",
  lname: "Montanna",
};

function greetUser(salutation, message) {
  console.log(`${salutation} ${this.fname} ${this.lname}!!! ${message}`);
}

greetUser.apply(person1, ["Mr", "Good Morning!"]);
// Mr Justin Case!!! Good Morning!

greetUser.apply(person2, ["Ms", "Good Morning!"]);
// Ms Parsley Montanna!!! Good Morning!

```

## `bind`

`bind` creates a new function and returns the function with the newly bound `this` context. It attaches the value of `this` passed as an argument to the function and returns the function.

Let us understand this with an example. We have an object named `user` and the function `greetUser()` within the object. Consider we have an admin role too and there is an object for that. Instead of defining a new function altogether, we can *bind* the `greetUser()` of the user object to the `admin` object and create a new function.

```JS
const user = {
  fName: "Kevin",
  sName: "Edwards",
}

const greetUser =  function() {
  console.log(`Welcome ${this.sName}, ${this.fName}`)
}

const admin = {
  fName: "John",
  sName: "Doe",
}

const greetAdmin = greetUser.bind(admin)

greetAdmin()
// Welcome Doe, John
```

## Polyfills for `call()`, `apply()` and `bind()`

Polyfill, on its own, is a very vast concept. It would be too much information in a single article. In short, polyfills allow us to provide modern JavaScript functionalities in browsers that don't support them.

`call()`, `apply()` and `bind()` are available on every function that we create in JavaScript. To make our own functions available in the same way, we have to define the functions on **Function Prototype**. For more information on what prototype is [read here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain).

### Polyfill for `call()`

`call()` method calls a function with a new this context and other arguments that the function being called expects. The function would look something as below.

```JS
Function.prototype.myCall = function(currentContext = {}, ...args) {
  currentContext.fn = this;
  currentContext.fn(...args);
};
```

Let us go through each line and understand what is happening. 
- We are creating a polyfill called `myCall` and adding it to the `Function.prototype` so that it is available on all the functions. 
   - `currentContext` is the object we want to call our function with
   - `...args` - taking the rest of the parameters
- We then create a property `fn` on the `currentContext` object.
- `this` points to the function that is invoking or calling the `myCall()` function
- We now call the function with its additional parameters by spreading them

Consider the below example where we are using the above polyfill

```JS
Function.prototype.myCall = function(currentContext = {}, ...args) {
  currentContext.fn = this;
  currentContext.fn(...args);
};

const person1 = {
  fname: "Justin",
  lname: "Case",
};

function greetUser(salutation, message) {
  console.log(`${salutation} ${this.fname} ${this.lname}!!! ${message}`);
}

greetUser.myCall(person1, "Mr", "Good Morning!");
// Mr Justin Case!!! Good Morning!

```
We are assigning the `greetUser()` function to `person1` object and redefining the `this` context to be the invoking object and calling it with the arguments.

### Polyfill for `apply()`

We have seen that `call()` and `apply()` are similar except that `apply()` accepts all additional arguments to be in an array. So their polyfills are also similar

```JS
Function.prototype.myApply = function(currentContext = {}, arg = []) {
  currentContext.fn = this;
  currentContext.fn(...arg);
};
```

Instead of accepting all other arguments using the rest operator, we use a second parameter that accepts an array.

```JS
Function.prototype.myApply = function(currentContext = {}, arg = []) {
  currentContext.fn = this;
  currentContext.fn(...arg);
};

const person2 = {
  fname: "Parsley",
  lname: "Montanna",
};

function greetUser(salutation, message) {
  console.log(`${salutation} ${this.fname} ${this.lname}!!! ${message}`);
}

greetUser.myApply(person2, ["Ms", "Good Morning!"]);
```

### Polyfill for `bind()`

`bind` returns a new function with a new `this` context. Before we delve into writing polyfill for `bind`, let's explore a bit more about how bind works

```JSX
const admin = {
  fname: "John",
  lname: "Doe",
};

const printDetails = function (city) {
  console.log(`${this.fname} ${this.lname} resides in ${city}`);
};

const printDetailsOfAdmin = printDetails.bind(admin, "Doha");
printDetailsOfAdmin()

// John Doe resides in Doha
```
In the above codeblock we have an object `admin` and a function `printDetails`. When we write the polyfill we need to keep in mind that we have to return a function that can be called later. This function can take additional arguments which need to be passed on when we are returning the function. With these things in mind, let's get started. We will iteratively improve our `bind()` polyfill so that things are not confusing.

#### Basic version of `bind()` polyfill

```JSX
Function.prototype.myBind = function (...args) {
  const obj = this;
  return function () {
    obj.call(args[0]);
  };
}
```
This is a very basic implementation of `bind()` where we are returning a function that calls the method on which are binding the new object i.e., the new `this` context. 

```JSX
const printDetails = function () {
  console.log(`${this.fname} ${this.lname}`);
};

const admin = {
  fname: "John",
  lname: "Doe",
};

const printAdminDetails = printDetails.myBind(admin)
```
Consider the above function and the subsequent call to myBind, then `this` in line 2 of the polyfill will be equal to the `printDetails()` function. We need to call the function with the object `admin` which can be done by accepting arguments using the rest operator and the first argument is the object which we need to pass accessible using `args[0]`. This version works fine as long as we don`t have any extra arguments. 

#### Avanced version of `bind()` polyfill

One of the very first changes that we need to do to pass other arguments too, is to replace `call` with `apply` the reason being we cannot keep track of how many extra arguments need to be passed. Now we need to pass the rest of the arguments apart from the object i.e., `args[0]` to the function that is getting returned

```JSX
Function.prototype.myBind = function (...args) {
  const obj = this;
  const params = args.slice(1);
  return function () {
    obj.apply(args[0], [...params]);
  };
};
```
Now let's try to see how it behaves when we use it on one of the objects 

```
const admin = {
  fname: "John",
  lname: "Doe",
};

const printDetails = function (city, country) {
  console.log(`${this.fname} ${this.lname} resides in ${city}, ${country}`);
};

const printDetailsOfAdmin = printDetails.myBind(admin, "Doha", "Qatar");
printDetailsOfAdmin()

// John Doe resides in Doha, Qatar
```
This totally works fine but consider the below scenario where we are passing the arguments when we are calling the `printDetailsOfAdmin()` function.


```JSX
const printDetailsOfAdmin = printDetails.myBind(admin, "Doha");
printDetailsOfAdmin("Qatar");

// John Doe resides in Doha, undefined
```

This is happening because we are not passing the arguments that we are receiving when the returned function gets called inside our polyfill.

```JSX
Function.prototype.myBind = function (...args) {
  const obj = this;
  const params = args.slice(1);
  return function (...callerFunctionArgs) {
    obj.apply(args[0], [...params, ...callerFunctionArgs]);
  };
};

```

This version of polyfill will work for all cases.

```JSX
const printDetailsOfAdmin = printDetails.myBind(admin, "Doha");
printDetailsOfAdmin("Qatar");

// John Doe resides in Doha, Qatar
```

That's all for this article. Thanks for reading. 

You can follow me on my [Twitter](https://twitter.com/ttahm3d) to be notified whenever I'm writing an article and keep updated with my journey in the world of web dev. Thanks for reading.




 
