Implementation principle of call and apply in JS

Keywords: Javascript

         There are two functions call and apply on the prototype chain of the Object object in JS. Their main function is to point this of the called function to the target Object, i.e

    Object.call(target_ele);

         In another article, I also elaborated on the usage of call and apply, so I won't go into details here.

         Here, call has two variables, one is the element pointed to by the changed this target, and the other is the parameter group passed to the Object function.

         Implementation idea:

         Here, because the Object object is a Function Object, we can add a call implementation method to the prototype chain Function of the Object object:

 Function.prototype.mycall = function() {

    }

         Since then, our preparations have been completed.

         In fact, in the call function, you point the of the objective function to the currently passed element. I wrote about this in previous articles on the pointing of different functions. At this time, this in our mycall function actually points to the calling function, for example:

 function User() {

    }

         We create a User function, and the User calls the mycall function:

 User.mycall();

         At this time, this in the mycall function actually points to the User function itself. Since we want to change the direction of this, we must re point to this, but re assigning this to the User is not allowed, so we borrow the prototype chain method again and add a fun method to the prototype chain of the target object:

 Function.prototype.mycall = function(target_ele) {
        target_ele.__proto__.fun = this;
    }

         At this time, the User function is attached to the prototype chain of our target object as an attribute method, and we rewrite the function body content in the User:

function User() {
        this.name = 123;
    }

         So that we can see the effect in the test. We call the added fun attribute and create an empty object a to test the method. At this time, the complete method code is as follows:

function User() {
        this.name = 123;
    }
Function.prototype.mycall = function(target_ele) {
        target_ele.__proto__.fun = this;
        target_ele.fun();
    }
let a = {};
User.mycall(a);
console.log(a)

         User calls the Function mycall created in the prototype chain of Function. Mycall needs to receive a target pair correlation, tracet_ Ele, after receiving, in tracet_ Mount the user Function in the prototype chain of ele object and call the mounted fun Function with the fun attribute. At this point, fun is a trap_ An attribute of the ele object, fun, points to the user constructor, so this in the user constructor is actually the target_ Ele object and set it as a tracer_ The ele object initializes an attribute named name, and the output result is:

         At this time, we have successfully implemented some functions of the call function. Next, we will implement the function transfer. Here, we use the expansion syntax added in ES6 to rewrite the mycall function as follows:  

 Function.prototype.mycall = function(target_ele, ...args) {
        target_ele.__proto__.fun = this;
        target_ele.fun(...args);
    }

         At this time, we add a variable name to be passed in the User constructor, and assign it as 123 when there is a value and no value

function User(name) {
        this.name = name || 123;
    }

         Then let's test:

User.mycall(a, 'Li Si');

         The output result is:

         So far, we have completed the implementation of the call function. Because our call actually has a return value and the fun attribute should not exist, we need to modify the mycall function:          

 Function.prototype.mycall = function(target_ele, ...args) {
        target_ele.__proto__.fun = this;
        target_ele.fun(...args);
        delete target_ele.fun;
        return target_ele;
    }

         Well, the above completes the implementation of call. The apply function is similar to the call function, which is to change the passed function into array form. We only need to rewrite it as follows:

Function.prototype.mycall = function(target_ele, args) {
        if (!(args instanceof Array))
            throw Error('Please pass an array!');
        target_ele.__proto__.fun = this;
        target_ele.fun(...args);
        delete target_ele.fun;
        return target_ele;
    }

         Add the judgment on the array and pass its parameters into the objective function in combination with the expansion syntax. Let's rewrite the User and add an age attribute:

 function User(name, age) {
        this.name = name || 123;
        this.age = age || 1;
    }

         Call mycall at the same time:

User.mycall(a, ['Li Si', 18]);

         The output result is:

         Summary: the above is the implementation of call and apply methods. Of course, many methods in the old version can also realize similar parameter passing. Here, only a relatively simple expansion syntax is selected. Those who are interested can do in-depth research by themselves.  

Posted by Geteburg on Sun, 19 Sep 2021 18:16:44 -0700