What is the bind

Quote MDN’s explanation

The function.prototype.bind () method creates a new Function. When bind() is called, this of the new Function is specified as the first argument to bind, and the remaining arguments are specified as arguments to the new Function to use when called

Bind does three main things

  • Returns a new function
  • The new functionthisPoint to thebindThe first argument to
  • The remaining arguments are passed in as arguments to the new function

Basic use

The simplest example

var tea = {
    value: 'tea'
}

function drink(name) {
    console.log(`${name} drink The ${this.value}`);
}

// return a function
// this is the first argument to the new function
// The remaining parameters are used as parameters of the new function
var drinkTea = drink.bind(tea, 'dongchuan');

drinkTea();  'Dongchuan drink milk tea'
Copy the code

Simple implementation of BIND

After analyzing the three things bind does, we can implement bind in a simple way. We will use ES6 to implement IT quickly and easily

/ / the first edition
function _bind(asThis, ... args1) {
    let fn = this;  // When the function is called, this is actually the calling function
    return function(. args2) {  // Also, the new function returned can accept arguments
        returnfn.call(asThis, ... args1, ... args2); }}Copy the code

The simulation realization of the construction effect

If you use the new operator to return a new function, your redefinition of this will not work.

See the blog link to JavaScript’s in-depth mock implementation of BIND #12

In fact, there is a description on MDN, but it is not written at the top

MDN’s introduction to using bind and then new

The binding function can also be constructed using the new operator, which shows that the target function has been built. The supplied this value is ignored, but the leading argument is still supplied to the simulation function

Important information extraction: The supplied this value is ignored

Let’s use an example to understand

var tea = {
    value: 'tea'
}

function drink(name) {
    console.log(`${name} drink The ${this.value}`);
}

var drinkTea = drink.bind(tea, 'dongchuan');

drinkTea();  'Dongchuan drink milk tea'

var obj = new drinkTea();  // 'dongchuan drink undefined'

console.log(obj.value);  // undefined

// With the new operator, this points to obj
Copy the code

Points to note:

  • drinkTeathethisIt’s still pointingtea
  • Pair binding functiondrinkTeaUse the new operator, originally boundthisIt’s going to failthisPoint to theobj

So, we need to check this for the new function that bind returns

  • If the new function isthisIs on the prototype of the new function, that is, using the constructor
    • (Here you can refer to another one aboutnewOperator implementation parsingHow do I implement a new operator)
    // Simply implement a new
    function _new(resultFn) {
        let tmp = {};
        tmp.__proto__ = resultFn.prototype;
        resultFn.call(tmp);
        return tmp;
        
        / / verification
        console.log(tmp instanceof resultFn);  // true
    }
    Copy the code
  • Otherwise it’s normal

Redesign returns the this pointer to the new function

// The second version supports new
function _bind(asThis, ... args1) {
    let fn = this;
    let resultFn = function(. args2) {
        console.log(this instanceof resultFn);  // True if the new operator is used
        return fn.call(this instanceof resultFn ? this: asThis, ... args1, ... args2); }// Create an instance that inherits the prototype of the binding function
    resultFn.prototype = fn.prototype;
    return resultFn;
}
Copy the code

The prototype optimization for the return function

Resultfn. prototype = fn. Prototype; if you set resultfn. prototype to fn

  • Workaround: Use an empty function as a relay
/ / the third edition
function _bind(asThis, ... args1) {
    let fn = this;
    let resultFn = function(. args2) {
        return fn.call(this instanceof resultFn ? this: asThis, ... args1, ... args2); }let fnNo = new Function(a); fnNo.prototype = fn.prototype; resultFn.prototype =new fnNo();  Resultfn.prototype.__proto__ === fnNo. Prototype === fn. Prototype;
    return resultFn;
}
Copy the code

Replace the ES6 implementation with ES5

All of the above bind implementations are implemented in ES6, but many of the new ES6 syntax may not be as well supported if you need to write bind by hand, so we’ll rewrite the implementation in ES5

  • Returns a new function
  • The new functionthisNormally points to the first parameter
  • Other arguments are passed to the new function
  • rightnewSupport, includingthisCannot be replaced by the first argument, supports binding functionprototypeProperty access

Rewrite using ES5

/ / the fourth edition
function _bind(asThis) {
    var fn = this;
    var args1 = Array.prototype.slice.call(arguments.1);
    var resultFn = function() {
        var args2 = Array.prototype.slice.call(arguments);
        return fn.apply(this instanceof resultFn ? this : asThis, args1.concat(args2));
    }
    
    var fnNo = new Function(a); fnNo.prototype = fn.prototype; resultFn.prototype =new fnNo();
    return resultFn;
}
Copy the code

The final code

Finally, add abnormal case judgment

  • Judgment callsbindIs theta a function
  • Use it online to see if it existsbind

So, the final implementation code is:

/ / the final version
function _bind(asThis) {
    // Determine if bind is a function
    if (typeof this! = ='function') {
        throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }
    var fn = this;
    var args1 = Array.prototype.slice.call(arguments.1);
    var resultFn = function() {
        var args2 = Array.prototype.slice.call(arguments);
        return fn.apply(this instanceof resultFn ? this : asThis, args1.concat(args2));
    }
    
    var fnNo = new Function(a); fnNo.prototype = fn.prototype; resultFn.prototype =new fnNo();
    return resultFn;
}

Function.prototype.bind = Function.prototype.bind || _bind;
Copy the code

Done!