If you are not familiar with the differences between call, apply, and bind, check out my previous articles. You can use call, apply and bind

If there are any mistakes, please point them out in the comments so I can correct them myself

Author: thomaszhou

The bind to realize

Normally we would use bind directly, but this time we’ll try to implement it with native JS

The bind() method creates a new function. When the new function is called, the first argument to bind() will be this when it runs, and the subsequent sequence of arguments will be passed as its arguments before the arguments passed

The bind function has three characteristics:

  • 1) Return a function: We can implement this using call or apply
  • (2) Arguments can be passed in: we handle with arguments
var foo = { value: 1 };

function bar(name, age) {
    console.log(this.value);
    console.log(name);
    console.log(age);
}

var bindFoo = bar.bind(foo, 'daisy'); / / (1)bindFoo is a functionbindFoo('18'); // 1 // Daisy //Copy the code

Implement the first two features: Implementation code (Version 1.0) :

Function.prototype.bind2 = function(context) { var self = this; / / to getbind2 function from the second parameter to the last parameter var args = Array. The prototype. Slice. The call (the arguments, 1);return function() {// Arguments are used to refer tobindFunction returnedbindThe parameter var passed in when Foo is calledbindArgs = Array.prototype.slice.call(arguments);
        self.apply(context, args.concat(bindArgs)); }}Copy the code
  • (3) A binding function can also use the new operator to create objects: this behavior is like treating the original function as a constructor. The supplied this value is ignored, and the arguments to the call are supplied to the mock function.: by modifying the prototype of the returned function
var value = 2;

var foo = {
    value: 1
};

function bar(name, age) {
    this.habit = 'shopping';
    console.log(this.value);
    console.log(name);
    console.log(age);
}

bar.prototype.friend = 'kevin';

var bindFoo = bar.bind(foo, 'daisy');

var obj = new bindFoo('18'); // this is obj // undefined // Daisy // 18 console.log(obj.habit); console.log(obj.friend); // shopping // kevinCopy the code

Note: Even though value is declared in both global and foo, undefind is returned, indicating that the bound this is invalid

Implement the third feature first: implementation code (Version 2.0) :

Function.prototype.bind2 = function (context) {
    letself = this; / / the self - > ƒbar() {}let args = Array.prototype.slice.call(arguments, 1);
    let fbound = function () {
      let bindArgs = Array.prototype.slice.call(arguments); // (1) When used as a constructor, this --> instance (fbound created instance), self --> bind function bar, the result istrue// (2) As a normal function, this -->window, self --> bind function, and the result isfalse, so self points to the bound context. self.apply(this instanceof self ? this : context, args.concat(bindArgs)); }; // In order to make this instanceof selftrueFbound. Prototype = this.prototype; fbound.return fbound;
};
Copy the code

Optimization: Implementation code (Version 3.0) :

Version 2.0 sets fbound.prototype = this.prototype. When we modify fbound.prototype, we will also modify bar prototype. At this point, we can use an empty function to do the transition, and then use combinatorial inheritance to implement

Function.prototype.bind2 = function (context) {
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);
    
    var fNOP = function() {}; // // defines an intermediate function to be used as an inherited intermediate value var fbound =function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        self.apply(this instanceof self ? this : context, args.concat(bindArgs)); } fNOP. Prototype = this.prototype; // Point the prototype method of the new function that fbound returns to the instantiated object of fNOP. Prototype = new fNOP(); fbound.prototype = new fNOP();return fbound;
}
Copy the code

In the code above, we introduced a new function is fun, to inherit the function prototype, and through the new operator to instantiate the instance of the object, it is for fBound prototypal inheritance, at this point, we make the new function inherited all attributes of the object function and the method, and ensure that won’t because the affect to the operation of the prototype chain function

------------- optimize three points -------------------------

  • (compatibility) This Function is added when there is no bind on the prototype chain of Function
Function.prototype.bind = Function.prototype.bind || function () {
    ……
};
Copy the code
  • Only functions can call bind, not other objects. That's whether this is a function.
if(typeof this ! = ='function') {
    throw new TypeError("NOT_A_FUNCTION -- this is not callable");
}
Copy the code
  • For code passed by arguments, you can replace it with ES6 extended operators

Final version!!

Function.prototype.bind = Function.prototype.bind || function(context, ... formerArgs) {let self = this;

    if(typeof this ! = ='function') {
			throw new TypeError("NOT_A_FUNCTION -- this is not callable");
	}
    let fNOP = function () {}; 

    let fbound = function(... laterArgs) { self.apply(this instanceof self ? this : context, formerArgs.concat(laterArgs)); // es6 : formerArgs.concat(laterArgs) }; fNOP.prototype = this.prototype; fbound.prototype = new fNOP();return fbound;
  };

Copy the code