The implementation of bind method

Posted by worldofcarp on Sun, 14 Jun 2020 08:07:18 +0200

Before reading this article, you should call or apply , the principle of the new operator, currification, and Prototype chain mechanism Have a certain understanding.

1. Principle of bind method

When fn(p1,p2,p3) calls the bind method, fn.bind(obj) will return a function bind FN with this pointing to obj. Bindfn is the same as the function body of FN. Moreover, bind can only receive some parameters of the original function, and let the returned function process other parameters, that is, function currification.

2. The first simple implementation of bind

Because the bind return value is a function, we need to consider the way to call the function. If the function is a constructor, should the result of the call be the same as that of the direct call through the new operator? In the process of implementing bind, do you need to judge the call mode of the function? I was confused about this, and then I first implemented a simple version without judging the way of calling:

    //Constructor not considered
        Function.prototype.myBind = function (thisValue, ...firstArgs) {
            //thisValue is the return value function this firstArgs to bind is the first passed in parameter array
            const currentFn = this;
            //After executing myBind, you get the same function body as the function body calling myBind, except that this is bound in the return value function 
            return function (...secondArgs) {
                //secondArgs is the second passed in parameter array
                return currentFn.apply(thisValue, [...firstArgs, ...secondArgs])
            }
        }
        function add(a, b, c) {
            console.log(this);
            return a + b + c
        }
        console.log(add(1, 2, 3));
        console.log(add.bind(null, 1)(2, 3));
        console.log(add.bind(obj, 1)(2, 3));
        console.log(add.myBind(null, 1)(2, 3));
        console.log(add.myBind(obj, 1)(2, 3));

But if it's a constructor, the result will be different

    function Person(n, a, s) {
            console.log(this);
            this.name = n
            this.age = a
            this.sex = s
        }
        Person('Xiaoming', 18, 'male')//this points to window
        const p = new Person('floret', 19, 'male')//this points to p
        const bindPerson = Person.bind(obj)
        const myBindPerson = Person.myBind(obj)
        const p2 = new bindPerson('xiaogou', 17, 'male')//Point to p2
        const p3 = new myBindPerson('xiaogou', 17, 'male')//Point to obj 

This is because although the new operator points this inside myBindPerson to the current instance p3 when new myBindPerson(), myBindPerson is executing the return statement currentFn.apply In the sentence (thisvalue, []), this is forced to bind to obj, so the final output of this is obj. Obviously, at this time, we should judge whether it is a function called by the new operator.
According to the principle of new operator, we can use the method of instanceof to judge.

3.bind implementation

    Function.prototype.myBind = function (thisValue, ...firstArgs) {
            //thisValue is the return value function this firstArgs to bind is the first passed in parameter array
            const currentFn = this;
            //Create an empty transit function to detect whether new is used
            const tempFn = function () { }
            //If currentFn is a constructor, its prototype maintains the prototype chain relationship by pointing the prototype of the transit function to the prototype of the current function
            if (currentFn.prototype) {
                tempFn.prototype = currentFn.prototype
            }
            //At this time, the instance created by tempFn is taken as the prototype of BindFn 
            //The__ proto__ It points to new Temp()
            //When new TempFn() is added to the prototype chain, you can judge whether the call to bindFn uses new through instanceof
            bindFn.prototype = new tempFn();//Key statements that can be judged by instanceof in the future
            function bindFn(...secondArgs) {
                //If the call uses the form of new, const p=new bindFn(), then this points to p because the instance of tempFn is the prototype object of P
                //So p instanceof tempFn should be true. At this time, we should not change this direction
                //If you call bindFn() this directly, it should point to window. At this time, we need to bind this
                return currentFn.apply(this instanceof tempFn ? this : thisValue, [...firstArgs, ...secondArgs])

            }
            return bindFn
        }
        Person('Xiaoming', 18, 'male')//this points to window
        const p = new Person('floret', 19, 'male')//this points to p
        const bindPerson = Person.bind(obj)
        const myBindPerson = Person.myBind(obj)
        const p2 = new bindPerson('xiaogou', 17, 'male')//Point to p2
        const p3 = new myBindPerson('xiaogou', 17, 'male')//Instance to p3 bindFn

4. mdn implementation of bind

I refer to mdn. The mdn implementation is:

if(!Function.prototype.bind){
  Function.prototype.bind = function(oThis){
    if(typeof this !== 'function'){
      throw new TypeError('The bound object needs to be a function')
    }
    var self = this
    var args = [].slice.call(arguments, 1)
    var func = function(){}
    fBound = function(){
      return self.apply(this instanceof func ? this : oThis, args.concat([].slice.call(arguments)))
    }
    if(this.prototype){
      func.prototype = this.prototype
    }
    fBound.prototype = new func()
    return fBound
  }
}

I use Statement promotion mechanism , will

    var func = function(){}
    if(this.prototype){
      func.prototype = this.prototype
    }
    fBound.prototype = new func()

Put together, in order to facilitate the understanding of the role of the transfer function func.

Only for personal understanding. If there is any mistake, please correct it.
If you think it will help you, please give me a compliment

Topics: Javascript