Many people know how these three methods work, but don’t know how they work

call

  • define
The call() method calls a function with a specified this value and separately supplied arguments (a list of arguments).Copy the code

let name='silly egg';

let person={
    name:'romin'
}

function say(){
    console.log('my name is'+this.name)
}

say.call(person);
// my name is romin
Copy the code

Call changes the this direction of the say function while the say function executes.

So let’s implement it

  • 1, change this to direct tosayFunction in thepersonObject.
  • 2, function execution, callpersonIn thesayfunction
let person={
    name:'romin'.say:function(){
        console.log(this.name)
    }
}
person.say();
Copy the code

Since the above method changes the structure of the Person object, the redundant say function needs to be deleted, and the delete person.say operation needs to be performed.

At this point, we know exactly what to do:

1, put the function to be executed (the function before call) into the specified object (the first argument of call);

2. Execute this function;

3. Delete the function added to the object.

Function.prototype.call = function(context){
    / / the first step
    // This is the function called by the generation
    context.fn = this;
    / / the second step
    context.fn();
    / / the third step
    delete context.fn;
}
Copy the code

The first argument in call could also be null, or it could be a string, and we need to process the context, otherwise we can’t mount the fn function on the string.

// Process the context
context = context?Object(context):window;
Copy the code

For example, a call may have multiple parameters, except context, and the rest of the parameters must be handled by say.

let name='silly egg';

let person={
    name:'romin'
}

function say(animal,age){
    console.log(animal)
    console.log(age)
    console.log('my name is '+this.name+'I belong to.'+animal);
}

say.call(person,'🐯'.15);
// My name is romin, I belong to 🐯
Copy the code

How do I pass the call parameters to SAY and make it execute? Argumentargument.call (); call (); call (); Spell out an argument string.

let args = [];
for(let i=1; i<arguments.length; i++){ args.push('arguments['+i+'] ')}// ['arguments[1]','arguments[2]']

// Args and string concatenation call its toString() equivalent to context.fn(arguments[1],arguments[1])
eval('context.fn('+ args +') ')
Copy the code

Complete implementation:

Function.prototype.call = function(context){
    // Process the context
    context = context?Object(context):window;
    // First step, mount function
    // This is the function called by the generation
    context.fn = this;
    
    // Step 2, prepare the arguments, and then let the function execute
    let args = [];
    for(let i=1; i<arguments.length; i++){ args.push('arguments['+i+'] ')}let result = eval('context.fn('+ args +') ')
    / / the third step
    delete context.fn;
    // Note that there may be a return value
    return result;
}

Copy the code

apply

  • define
The apply() method calls a function with a given this value and the arguments supplied as an array (or array-like object).Copy the code

Apply and Call are similar, except that they pass different parameters

Function. Prototype. Apply = Function (context,arr){ Object(context):window; Context.fn = this; context.fn = this; context.fn = this; let result; if(! arr){ result = context.fn(); }else{ let args = []; for(let i=0; i<arr.length; Arr i++) {args. Push (' (' + I + ') ')} the result = eval (' context. The fn (' + args + ') ')} / / step 3 delete context. The fn; Return result; }Copy the code

bind

  • Definition:
The 'bind()' method creates a new function that sets the 'this' keyword to the supplied value when called. And when the new function is called, the given parameter list is taken as the first several items of the parameter sequence of the original function.Copy the code

Features: Change this to return a function (higher order function) that can pass in arguments;

let name='silly egg';

let person={
    name:'romin'
}

function say(animal,age){
    console.log(animal)
    console.log(age)
    console.log('my name is '+this.name+'I belong to.'+animal);
}

let rominsay = say.bind(person,'🐯');
rominsay(15);
Copy the code

Its simulated implementation, if not with argument list:

Function.prototype.bind = function(context){
    let that = this;
    return function(){
        // Also be aware of possible return values
        returnthat.apply(context); }}Copy the code

Second edition, if there are parameters:

Function.prototype.bind = function(context){
    let that = this;
    // Get the parameter list
    let bindArgs = Array.prototype.slice.call(arguments.1);
    return function(){
        // Get the argument list at the time of the call
        let args = Array.prototype.slice.call(arguments);
        // Also be aware of possible return values
        returnthat.apply(context,bindArgs.concat(args)); }}Copy the code

The following example uses the native bind method:

let person={
    name:'romin'.gender:'male',}function Say(name,age){
    this.habit = 'Work paddle';
    console.log(this.gender);
    console.log(name);
    console.log(age);
}

Say.prototype.friend = 'silly egg';

let RominSay = Say.bind(person,'romin');

let say = new RominSay('15');

console.log(say.friend) / / silly eggs
console.log(say.gender);// undefined
console.log(say.habit)// Go to work

Copy the code

If you use your own implementation, the result is

console.log(say.friend) // undefined console.log(say.gender); // undefined console.log(say.habit)// undefinedCopy the code

So there are three questions:

  • 1. Native methodsgenderThe failure
  • I wrote it myselffriendAttribute did not inherit successfully;
  • 3. I wrote it myselfhabitNor did I get it;

But please note the following:

If the bound function is new, then this in the returned function is an instance of the current functionCopy the code

If RominSay is created with a new ‘say’, this is the ‘say’ instance of RominSay (instead of ‘person’), and the friend attribute is found through the prototype chain.

Modify the above implementation method

Function.prototype.bind = function(context){
    let that = this;
    // Get the parameter list
    let bindArgs = Array.prototype.slice.call(arguments.1);
    function newFun(){
        // Get the argument list at the time of the call
        let args = Array.prototype.slice.call(arguments);
        // When newFun is the constructor, this points to the instance; if not, this also points to the context
        return that.apply(this instanceof newFun ?this:context,bindArgs.concat(args));
    }
    // Change the return function's prototype to the binding function's prototype, and the instance inherits the value from the binding function's prototype
    newFun.prototype = this.prototype;
    return newFun;
}

Copy the code