js advanced - understand the principle of call()

 1. Definition of call():

The call() method calls a function or method on the premise of using a specified this value and several specified parameter values.

The point is that we can set this to point to another object, so the data and methods in this object can be accessed

If you want to use the FN1 method in the a object to process the data in the B object, you can use FN1 call(b);

var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call(foo); // 1
// Here, if you call bar (); This is because the function execution context is created when bar() is called, and this points to the object window that calls it Value is to find value in the window
// No value defined, undefined
// And bar call(foo); Point this in the bar to the foo object. When calling, first find out whether there is a value attribute in the foo object, and the output is 1

2. Function of call (obj)

(1) Changed the direction of the function active object this, pointing to the new object foo

(2) The function is then executed

 

3. Simulate the call() function

First of all, call wants a object to use the method of b object, so we directly add a new attribute in a object to store the method, and then call the method in A. because it is called by a object, the method this points to a object,

After the execution result is obtained, delete the attribute to complete the function

  • Set function to object property
  • Execute the function properties
  • Delete the attribute
1:
foo.fn = bar
2:
foo.fn()
3:
delete foo.fn

 

3.1 call() in the first version

The basic function of call() is realized

//Because it is called by all function objects call()method,So it should be written on the prototype object of the function
According to the prototype chain fn._proto_ === Function.prototype ,definition call method

Function.prototype.call = function(context){
    //1.call Will receive parameters context,by this Reoriented object
    //2.How to get the called function? 
    // from bar.call(obj)knowable,This is a function instance object bar,call call method,In a function called by an object this Point to the object,therefore call Internal this point bar,This gives you the function to call
    
    context.fn = this; // hold bar Endow obj.fn attribute
    
    context.fn(); // If not context call,that fn()Medium this Will point directly to window
    
    delete context.fn;
    //delete fn attribute
}

However, call() itself can receive multiple parameters for processing, not only modify the pointed object, but also continue to modify

3.2 call() in the second edition

Question 1: because the function calling call() needs to receive different parameters, the actual problem is that call() needs to receive an indefinite number of parameters

obj object + multiple parameters, we can call the arguments object inside call(), get all the pseudo array of received parameters, remove the first one, then adjust to the parameter array of fn.

(1) Take all values with index 1 from arguments and put them into an array

// Take the above examples as an example arguments Is:
// arguments = {
//      0: foo,
//      1: 'kevin',
//      2: 18,
//      length: 3
// }
// because arguments Is a class array object, so you can use for loop
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
// arguments is an array like object, and its length is calculated in real time, so the length needs to be saved before traversal, or it will be calculated again in each cycle to filter out the first parameter args.push(
'arguments[' + i + ']'); } // After execution args by [foo, 'kevin', 18]

(2) Put the array as a formal parameter in fn()

Call the eval() method, construct a js statement execution block, and implement the args parameter variable with string addition

eval('context.fn(' + args +')') 
// Actually executed context fn(args.toString())

args is implicitly stringed

// Second Edition
Function.prototype.call2 = function(context) {
    context.fn = this;
    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }
    eval('context.fn(' + args +')');
    delete context.fn;
}

3.3 Third Edition

Question 1 when the input is null, this points to window

You only need to make a default judgment. It is recommended to use or our short circuit

  var context = context || window; //If the input is null and empty, they all point to window by default 
// The assignment logic is that with or without (priority value) 𞓜 default value (default value), the priority value can be taken and the default value can be taken

Question 2 the function has the return value return. Call() needs to output the contents of return

Create a variable to receive the return value processed by eval(), and finally add return

  var result = eval('context.fn(' + args +')');

    delete context.fn
    return result;

 

The above is the call() method based on es3 implementation

3.4 call() method based on es6 implementation

Because es6 implements the extension operator rest # so instead of using the eval() method to execute the function, you just put Args added to context fn(...args)

// ES6 call realization
Function.prototype.es6Call = function (context) {
  var context = context || window;
  context.fn = this;
  var args = [];
  for (var i = 1, len = arguments.length; i < len; i++) {
    args.push('arguments[' + i + ']');
  }
  const result = context.fn(...args);
  return result;
}

 

Added by amansabharwal on Mon, 03 Jan 2022 06:02:10 +0200