Trying to replicate .bind() in JavaScript

Viewed 69

I am learning JavaScript and I am trying to replicate some of the built-in methods like .push() .indexOf() etc..

So far so good and it is helping me a lot to learn the core of JavaScript.

But I came across .bind() and I can´t figure out a way of knowing the function that is calling the .bind() and tell my new .bind2() method to use it with the new THIS from our Object.

This is what I am trying:

const human = {
  firstName:"Humano",
  lastName: "Doe",
  fullName: function() {
    return this.firstName + "  " + this.lastName;
  }
};

const member = {
  firstName:"Member",
  lastName: "Nilsen",
};

Function.prototype.bind2 = function(obj) {
        if (typeof obj !== "object" ) {throw `${obj} is not an Object, I will work hard to return undefined later on`};
        return function(){
                return obj;
        }
    };

let testBind2 = human.fullName.bind2(member);
console.log(testBind2()); // {firstName: 'Member', lastName: 'Nilsen'}

So all I could do is to return the THIS from the Object passed as an argument but I have no idea how I can grab inside the .bind2() the fullName method (or any method that we want to bind) from the object that is actually calling the method .bind2().

Once we could grab the fullName we can pass it to bind2() and tell it to use THIS from our object.

This is only for the sake of learning and it would be amazing if someone could tell me if it is possible and how.

Thank you

1 Answers

don't modify window

bind is part of Function.prototype, so you can try implementing it there. Here is a possible polyfill -

Function.prototype.bind2 = function bind2(context, ...a) {
  return (...b) => this.call(context, ...a, ...b)
}

const human = {
  firstName:"Humano",
  lastName: "Doe",
  fullName: function() {
    return this.firstName + "  " + this.lastName;
  }
};

const func = human.fullName.bind2(human)

console.log(func())

Humano Doe

binding arguments

bind can receive arguments when calling bind and also when the bound function is called. Below we demonstrated that myfunc.bind2 has the exact same behavior as myfunc.bind -

Function.prototype.bind2 = function bind2(context, ...a) {
  return (...b) => this.call(context, ...a, ...b)
}

function myfunc(a,b,c) {
  console.log("name", this.name)
  console.log("sum", a + b + c)
}

const human = { name: "julio" }

const hello1 = myfunc.bind(human, 10)
const hello2 = myfunc.bind2(human, 10)

hello1(20, 30)
hello2(20, 30)

name julio
sum 60
name julio
sum 60

not just for objects

Do not make the assumption that bind only works with objects. You can bind any variable to the context -

Function.prototype.bind2 = function bind2(context, ...a) {
  return (...b) => this.call(context, ...a, ...b)
}

function myfunc(times) {
  return this.toUpperCase().repeat(times)
}

const foo = myfunc.bind2("hello")

console.log(foo(10))

HELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLO

without call

In your comment you asked how to write bind without using Function.prototype.call. If you want to include context in your binding, the only other option is to use Function.prototype.apply -

Function.prototype.bind2 = function bind2(context, ...a) {
  return (...b) => this.apply(context, a.concat(b))
}

function myfunc(times) {
  return this.toUpperCase().repeat(times)
}

const foo = myfunc.bind2("hello")

console.log(foo(10))

without context

.call and .apply give you the ability to set the this context for the bound function. You can instead add a context parameter. Choose any name for it that suits you -

Function.prototype.bind2 = function bind2(...a) {
  return (...b) => this(...a, ...b)
}

function myfunc(context, times) {
  return context.toUpperCase().repeat(times)
}

const foo = myfunc.bind2("hello")

console.log(foo(10))

any other way?

Is .call and .apply the only way to set the context? For all intents and purposes, yes. However, you can write a very bad hack if you just want to see how something like this could be possible. One caveat of this approach is that it only works for object contexts, not simple primitives like number or string -

// ⚠️ demo only, never do this
Function.prototype.bind2 = function bind2(context, ...a) {
  if (typeof context != "object") {
    throw Error("context must be an object")
  }
  return (...b) => {
    const bound = Symbol()
    context[bound] = this
    const value = context[bound](...a, ...b)
    delete context[bound]
    return value
  }
}

function myfunc(a,b,c) {
  console.log("name", this.name)
  console.log("sum", a + b + c)
}

const hello = myfunc.bind2({ name: "julio" }, 10)

hello(20, 30)

name julio
sum 60
Related