First of all, call and apply. The first parameter is the change of this direction. The writer is who. If it is in non strict mode, null or undefined direction is also window. The only difference between them is that when the function is executed, the parameters are passed in different ways. Call is passed one by one, and apply is to pass the parameters to be passed in the array as a whole.   ·func.call([context], x, y)   ·func.apply([context], [x, y])   Look at bind again. It and call and apply both change this and pass some parameters. Unlike call and apply, when they change this, they execute the function directly. Bind does not execute the function immediately.  
let obj = {
    fn(x, y) {
        console.log(this, x, y);
    }
};
        
obj.fn.call({}, 10, 20); // {}, 10, 20
obj.fn.apply(window, [10, 20]); //window, 10, 20

setTimeout(obj.fn.bind(30, 10, 20), 1000); //Number(30), 10, 20

 

 

First try to write bind:

In terms of parameters, the first is to pass a this point and need to do some processing. There are several parameters in the future

function bind(context) {
    //context May be null or undefined,It needs to be dealt with
    if (context == undefined) {
        context = window;
    }
    //Using slice method of array to combine argumentsGet the delivery specification this Parameter set after
    var args = [].slice.call(arguments, 1);
}

 

This in the bind function refers to the function to be executed finally, and a new anonymous function will be returned when bind is executed. In this new function, the function to be executed finally is this, and the direction of this will be changed:

function bind(context) {
    //context May be null or undefined,It needs to be dealt with
    if (context == undefined) {
        context = window;
    }
    //Borrowed array slice Combination of methods arguments Get the delivery specification this Parameter set after
    var args = [].slice.call(arguments, 1);
    //Function to be executed finally
    var _this = this;
    return function anonymous() {
        _this.apply(context, args);
    };
}    

 

This bind function is basically finished, but there are still some problems, such as event binding for elements, div.onclick = obj.fn.bind(window, 10, 20) , when the element is clicked, there will be ev event object, which is equivalent to the anonymous function returned by the bind function. The number of parameters is uncertain. Of course, the prototype method needs to be rewritten at last:

function bind(context) {
    //context May be null or undefined,It needs to be dealt with
    if (context == undefined) {
        context = window;
    }
    //Borrowed array slice Combination of methods arguments Get the delivery specification this Parameter set after
    var args = [].slice.call(arguments, 1);
    //Function to be executed finally
    var _this = this;
    return function anonymous() {
        var amArg = [].slice.call(arguments, 0);
        _this.apply(context, args.concat(amArg));
    };
} 
Function.prototype.bind = bind;

In this way, the rewrite of the bind function is finished.

Let's rewrite it with es6:

function bind(context = window, ...args) {
    return (...amArg) => this.call(context, ...args.concat(amArg));
}

The code seems to be a lot of simplification, of course, it can also be used to apply, but after testing, the performance is not as good as call.

 

Let's look at rewriting call:

It will have several parameters. The first is this to point to, which is written directly in es6:

function call(context = window, ...args) {

}

 

This in the function is the function that calls the call method. If you want the function to execute and this in the function points to the context passed in, it looks like context. The function can:

function call(context = window, ...args) {
    //to context Add one more $fn Attribute, assign the current function to this attribute      
    context.$fn = this;
    //Give Way context.$fn This method is executed before this Function execution, and this Pointing to context
    let result = context.$fn(...args);
    //Method should be deleted after adding 
    delete context.$fn;
    return result;
}
Function.prototype.call = call;

 

So Apple came out:

function apply(context = window, args) {
    context.$fn = this;
    let result = context.$fn(...args);
    delete context.$fn;
    return result;
}
Function.prototype.apply = apply;

 

There are still two problems here. One is that the $fn attribute has not been deleted, and there is no solution. The other is that the context passed in must be a reference type, but it can be a basic type:

function call(context = window, ...args) {
    context === null ? context = window : null;
    let type = typeof context;
    if (type !== "object" && type !== "function" && type !== "symbol") {
        //=>Basic type value
        switch (type) {
            case 'number':
                context = new Number(context);
                break;
            case 'string':
                context = new String(context);
                break;
            case 'boolean':
                context = new Boolean(context);
                break;
        }
    }
    context.$fn = this;
    let result = context.$fn(...args);
    delete context.$fn;
    return result;
}

 

The judgment of apply will not be written, but the rewriting is basically implemented. Of course, these methods are built-in write methods of js after all, we just want to roughly implement their implementation principles.