DEV Community

Cover image for Creating your own bind() (Polyfill of bind)
Uddesh
Uddesh

Posted on

Creating your own bind() (Polyfill of bind)

So, what is polyfill?

Polyfill is a fallback for a method that is not supported by the browser by default. You can find browser support for any function or method on the mdn website.

Now, what is bind()?

According to mdn

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

This definition sounds really fancy, but what does that mean?

In simple words,

The bind() takes an object as an argument and returns a new function whose this refers to the object we passed as an argument.

Now, we got the idea about polyfill and bind(). So, let's try to implement it.

1). let's create an object that we are going to use as a call site.

let obj = {
  name: 'Jack',
};
Enter fullscreen mode Exit fullscreen mode

2). Create a function that we need to bind with the object.

let myFunc = function () {
  console.log(`${this.name}`);
};
Enter fullscreen mode Exit fullscreen mode

If you call this function now, It will print undefined

3). Add your bind() to the function prototype.

Function.prototype.myBind = function (obj) {
  let func = this;
  return function () {
    func.apply(obj);
  };
};
Enter fullscreen mode Exit fullscreen mode

Putting it all together.

let obj = {
  name: 'Jack',
};

let myFunc = function () {
  console.log(`${this.name}`);
};

Function.prototype.myBind = function (obj) {
  let func = this;
  return function () {
    func.apply(obj);
  };
};

let newFunc = myFunc.myBind(obj)
newFunc() // Jack
Enter fullscreen mode Exit fullscreen mode

This is the basic implementation of bind(), But it has few edge cases. Let's say you want to pass some arguments in myBind(). How will you do that? Currently, we are not accepting arguments other than the object itself. Let's implement this functionality.

The problem

let obj = {
  name: 'Jack',
};

let myFunc = function (id) {
  console.log(`${this.name}, ${id}`);  // id will be undefined
};

Function.prototype.myBind = function (obj) {
  let func = this;
  return function () {
    func.apply(obj);
  };
};

let newFunc = myFunc.myBind(obj, 'a_random_id')
newFunc() // Jack, undefined
Enter fullscreen mode Exit fullscreen mode

We are trying to pass id in myBind but not able to access it.

The solution

let obj = {
  name: 'Jack',
};

let myFunc = function (id) {
  console.log(`${this.name}, ${id}`);  // id will be undefined
};

// Accepting any number of arguments passed to myBind
Function.prototype.myBind = function (obj, ...args) {
  let func = this;
  return function () {
    func.apply(obj, [...args]);
  };
};

let newFunc = myFunc.myBind(obj, 'a_random_id')
newFunc() // Jack, a_random_id
Enter fullscreen mode Exit fullscreen mode

Now we solved an edge case. However, there is one more improvement we can make. What if we want to pass arguments to newFunc()?

The problem

let obj = {
  name: 'Jack',
};

let myFunc = function (id, city) {
  console.log(`${this.name}, ${id}, ${city}`);  // id will be undefined
};

// Accepting any number of arguments passed to myBind
Function.prototype.myBind = function (obj, ...args) {
  let func = this;
  return function () {
    func.apply(obj, [...args]);
  };
};

let newFunc = myFunc.myBind(obj, 'a_random_id')
newFunc('New York') // Jack, a_random_id, undefined
Enter fullscreen mode Exit fullscreen mode

We are passing 'New York' but not able to access it inside myFunc

The solution

let obj = {
  name: 'Jack',
};

let myFunc = function (id, city) {
  console.log(`${this.name}, ${id}, ${city}`);  // id will be undefined
};

// Accepting any number of arguments passed to myBind
Function.prototype.myBind = function (obj, ...args) {
  let func = this;
  // Accepting arguments passed to newFunc
  return function (...newArgs) {
    func.apply(obj, [...args, ...newArgs]);
  };
};

let newFunc = myFunc.myBind(obj, 'a_random_id')
newFunc('New York') // Jack, a_random_id, New York
Enter fullscreen mode Exit fullscreen mode

Now I think we have covered almost all the cases. But, still, if there is anything left, Let me know in the comment section.

Time to celebrate. You have implemented your own bind().

I hope this will be helpful for you.

Bye

Top comments (13)

Collapse
 
hasnaindev profile image
Muhammad Hasnain

Great article, although, I don't think there is any need for this. It is 2021 and support for bind came between 2009 and 2011. Almost all operating systems for all types of devices comes with advanced built-in browsers that supports at least ES6 and almost all of them install and use modern browsers.

Collapse
 
uddeshjain profile image
Uddesh

Totally agree, But first of all it helps us to understand how bind() works behind the scene. Apart from that i was asked to implement polyfill of bind() in few interviews as well.

Collapse
 
davidmatossalvador profile image
David Matos salvador

this example is to understand how the black box works

Collapse
 
neer17 profile image
Neeraj Sewani

Nice article Uddesh.

Collapse
 
alekseiberezkin profile image
Aleksei Berezkin

Implementing bind with apply is kind of cheating πŸ˜‰

Collapse
 
adam_cyclones profile image
Adam Crockett πŸŒ€

I haven't used bind very often since arrow functions

Collapse
 
uddeshjain profile image
Uddesh

Arrow function are really helpful for adopting β€˜this’ from their parent and in most of the use cases we don’t need β€˜bind()’, But what if you want β€˜this’ to refer to a particular object then you need to use β€˜bind()’. I hope you got my point.

Collapse
 
adam_cyclones profile image
Adam Crockett πŸŒ€

To be honest, I only use bind for it's less well known second Param. I avoid using this and prefer pure functions, I have very few reasons to make this do anything, bimut that's just me ☺️

Collapse
 
prashan81992916 profile image
prashanth

This is a pretty cool post.

I would like to make a small alteration in your code.
We can eliminate the need for storing 'this' in the parent scope and using it at the time by invocation by making use of arrow function.

Function.prototype.myBind = function (obj, ...args) {
  return (...newArgs) => this.apply(obj, [...args, ...newArgs]);
};
Enter fullscreen mode Exit fullscreen mode

Hope this helps!!

Collapse
 
rahul4dev profile image
Rahul Singh
const module = {
  x: 42,
  getX: function () {
    return this.x;
  },
};

const unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// Expected output: undefined

const boundGetX = unboundGetX.myBind(module);
console.log(boundGetX());
// Expected output: 42
Enter fullscreen mode Exit fullscreen mode

This myBind does not give the expected output. It gives undefined for both consoles.

Collapse
 
esakki profile image
esakki29

Why because there is no return in your myBind return function. To fix this add return before func.apply line.

Collapse
 
abhishekjain35 profile image
Abhishek Jain

Good one. 🌟🌟

Collapse
 
uddeshjain profile image
Uddesh

Thanks. πŸ™‚