Function, as a built-in JavaScript object, has the following two methods:

  • Function.call();
  • Function.apply();

Both methods do the same thing: call the function as an imaginary method. (quoted from P.768 function.call (), the Definitive guide to JavaScript). The difference between the two is the type of parameter.

The specific differences between the two are not the focus of this article. Readers can search for them if they want to know.


Without further discussion, this article will further explain the inner workings of function.call () functions by implementing functions like function.call ()!

Requirement analysis for function.call ()

Before studying the internal operation mechanism of the function, let’s first understand what the function is intended to achieve.

According to the JavaScript Definitive Guide p.768 on function. call(thisobj, args…) , which is explained in detail as follows:

Call () calls the specified function as a method on the object thisobj and passes in the arguments after thisobj in the argument list. Returns the return value from the function call. In the body of the function, the keyword this refers to thisobj object, or if thisobj bit is null, the global object is used.

This means that the call() function will call the specified function with thisobj and return the result.

Design Function. The prototype. MyCall () method

With this description in place, we are ready to design our approach! Let’s start with the pseudocode:

Function.prototype.myCall = function(obj) {
    letObject = If an object obj is passed in, keep obj, otherwise add a temporary property to object for the global object fn assigns this to fn // make the object to which this points (the caller of the function) an object property. Pass the parameter to Object.fn () and execute the method, keeping the return value. Delete the temporary fn property on object. Returns a previously reserved value. }Copy the code

The realization of the Function. The prototype. MyCall ()

After having the previous design, is to follow the gourd gourd, the implementation of pseudo-code. The concrete implementation is as follows:

Function.prototype.myCall = function(obj) {
  let object = obj || global; // Global objects in the Node environment
  object.fn = this;
  var args = [];
  for (let i = 1, len = arguments.length; i < len; i++) {
    args.push("arguments[" + i + "]");
  }
  let result = eval("object.fn(" + args + ")");
  delete object.fn;
  return result;
};
Copy the code

The test of the Function. The prototype. MyCall ()

Two examples will be listed here, one to test the implementation of myCall() and the other to deepen your understanding of the call() function!

  1. Example 1 is a simple function call, using the function object fn2() to call the method fn1(a) as follows:

    function fn1(a) {
      console.log(a);
    }
    
    function fn2() {
      console.log("*");
    }
    
    fn1.myCall(fn2, "HelloWorld!"); / / output HelloWorld!
    Copy the code

    In this code, finally we are going to run fn1.myCall(fn2, “HelloWorld!” ).

    When running this code, this in myCall() refers to caller Fn1. The first argument to myCall() takes arguments’ first argument, fn2, and assigns it to the object variable. We add the object to which this points as the temporary fn property of objec. We then execute object.fn(), which is essentially executing the temporary addition of this to Object, fn1. We pass the arguments passed in to object.fn() from the second and execute, leaving the return value intact. Finally, the temporary attribute fn is destroyed, returning the reserved value.

  2. Case 2 is the serial call of myCall, the specific code is as follows:

    function fn1() {
      console.log(1);
    }
    function fn2() {
      console.log(2);
    }
    
    fn1.myCall.myCall(fn2); 2 / / output
    Copy the code

    This code is neither complicated nor complex to understand, but the key is to understand who is calling whom.

    • First the function executes from left to right, looking for fn1.mycall.mycall (fn2); In bold.

      In the first half, is actually a process of object properties to find, finally based on the prototype chain (default readers to understand what is the prototype chain here, if not clear prototype chain, please find information) lookup to exist in the Function in the prototype chain. The prototype. MyCall properties, the actual is a way. So the above code can actually be equal to the other line:

      // These two lines of code mean the same thing!
      fn1.myCall.myCall(fn2);
      Function.prototype.myCall.myCall(fn2);
      Copy the code
    • Here we can directly use the Function. The prototype. MyCall. * * myCall (fn2) * * to explain.

      The second step will execute the last part in bold.

      • First, we assign fn2 to the temporary variable object.
      • This points to the object is a Function object: Function. The prototype. MyCall (). So is assigned to the object. The temporary attribute points to is the Function of fn. Prototype. MyCall () method.
      • Since arguments length is 1, call the object.fn() method directly, which is object.myCall(), which is fn2.mycall ().
      • Fn2.mycall () actually executes global.fn2(), so it prints 2 without returning a value.
      • Destroys the temporary attribute fn.
      • The function completes.

      Here we paste some test code to help you understand:

      fn1.myCall.myCall(fn2); 2 / / output
      Function.prototype.myCall.myCall(fn2); 2 / / output
      fn2.myCall(); 2 / / output
      Copy the code

2019/03/29

AJie