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.