Write a call/bind/apply

How does it work?

//apply 
func.apply(thisArg, [argsArray])

//call
fun.call(thisArg, arg1, arg2, ...)

//bind
const newFun = fun.bind(thisArg, arg1, arg2, ...)
newFun()
Copy the code

Apply and call pass different arguments, but both are functions that are called at the same time, while bind returns a function that is bound to this.

This points to the

1. Default binding

function test() {
    // Undefined in strict mode
    // Non-strict mode is Window
    console.log(this);
}
setTimeout(function () {
    //setTimeout is special
    // Both strict and non-strict modes are Windows
    console.log(this);
});

arr.forEach(function () {
    // Undefined in strict mode
    // Non-strict mode is Window
    console.log(this);
});
Copy the code

2. Implicit binding this common point can be summed up in one sentence: whoever calls refers to whom

const obj = {
    name:'joy'.getName(){
        console.log(this); //obj
        console.log(this.name); //joy}}; obj.getName();Copy the code

3. Display bind call,apply,bind

const obj1 = {
    name: 'joy'.getName() {
        console.log(this); 
        console.log(this.name); }};const obj2 = {
    name: 'sam'
};

obj1.getName.call(obj2); //obj2 sam
obj1.getName.apply(obj2); //obj2 sam
const fn = obj1.getName.bind(obj2);
fn();//obj2 sam
Copy the code

4. The new binding

function Vehicle() {
    this.a = 2
    console.log(this);
}
new Vehicle(); // This refers to the object that comes out of Vehicle
Copy the code

The arrow function in ES6 is special. The arrow function this is this in the parent scope, not this in the call. Remember that the first four methods are determined on call, which is dynamic, whereas the arrow function’s this pointer is static, which is determined when it’s declared. More in line with js lexical scope

window.name = 'win';
const obj = {
    name: 'joy'.age: 12.getName: () = > {
        console.log(this); // The parent scope of this is window, so it is window
        console.log(this.name); //win 
    },
    getAge: function () {
        // call obj.getAge, where this refers to obj
        setTimeout(() = > {
            // So this also refers to obj so the result is 12
            console.log(this.age); }); }}; obj.getName(); obj.getAge();Copy the code

This priority

Arrow functions -> New bind -> Show bind call/bind/apply -> Implicit bind -> Default bind

Implement Call ()

Function.prototype.myCall = function(context) {
    context = context || window  
    context.fn = this
    const args = [...arguments].slice(1)
    constresult = context.fn(... args)delete context.fn
    return result
}
Copy the code

Implement apply ()

Function.prototype.myApply = function (context, args) {
    // You can also use es6 to set default parameters for parameters
    context = context || window
    args = args ? args : []
    // Add a unique attribute to the context to avoid overwriting the original attribute
    const key = Symbol()
    context[key] = this
    // Call the function with an implicit binding
    constresult = context[key](... args)// Delete the added attribute
    delete context[key]
    // Returns the return value of the function call
    return result
}
Copy the code

Implement bind ()

The difference between bind and apply is that bind returns a bound function, while apply calls directly. In fact, the implementation is very simple to think about, just return a function, which performs the above apply operation. However, there is a judgment point, because when you return a new function, you need to consider the use of new to call, and new has a higher priority, so you need to judge the call of new. Another feature is that bind can pass parameters when calling, and the new function generated after the call can also pass parameters, the effect is the same. So I’m going to have to do something here because I’ve already implemented apply, so I’m going to borrow it, and actually not borrow it is just copy the code

Function.prototype.myBind = function (context, ... args) {
    const fn = this
    args = args ? args : []
    return function newFn(. newFnArgs) {
        if (this instanceof newFn) {
            return newfn(... args, ... newFnArgs) }return fn.apply(context, [...args,...newFnArgs])
    }
}
Copy the code