Two JS handwriting questions a day—manually implement the call, apply, and bind functions in JS

Functions in JavaScript are first-class citizens and can be passed around and used like ordinary variables. This flexibility allows functions to be used in various scenarios, such as invocation of object methods, creation of constructors, and prototypal inheritance. During the use of functions, call, apply, bind are some of the more commonly used methods in JavaScript, which can change the context of function execution. You can also pass parameters.

In this article, we will introduce the three methods call, apply, bind in detail, and manually simulate and implement them.

call method

The call method is used to call a function, and can set the value of this inside the function. It allows you to point the this object of a function to any object, and pass in any number of parameters.

Below is the basic syntax of the call method:

function. call(thisArg, arg1, arg2, ...)

in:

  • function: the function to be called.
  • thisArg: Sets the value of the this object in the function function.
  • arg1, arg2, ...: There can be multiple parameters passed to the function function.

Here is a simple example:

const person = {<!-- -->
  name: 'Zhang San',
  sayHi() {<!-- -->
    console.log(`Hi, my name is ${<!-- -->this.name}.`);
  },
};

person.sayHi(); // output "Hi, my name is Zhang San."

const otherPerson = {<!-- -->
  name: 'Zhang San',
};

person.sayHi.call(otherPerson); // output "Hi, my name is Zhang San."

In the above example, we created a person object, which has a sayHi method for outputting self-introduction. We use the call method to bind the sayHi method of the person object to the otherPerson object, and output the otherPerson The name of the object.

Next, we’ll manually mock the implementation of the call method, implementing a function called myCall .

Function.prototype.myCall = function(context, ...args) {
  context = context || window;
  context.fn = this;
  const result = context.fn(...args);
  delete context.fn;
  return result;
};

In the above code, we added a new method myCall to Function.prototype. This method receives two parameters, the first parameter context indicates the this value to be bound, and the second parameter args indicates the value to be passed to The parameter list for the function. If the first parameter context is empty, it will be bound to the global object window by default.

Next, we added a fn attribute to the bound object context, and bound the current function to this attribute, then called the function, and saved the result into the result variable.

Finally, we remove the fn attribute on the context object and return the result. In this way, we have successfully manually mocked the implementation of the call method.

apply method

The syntax of the apply function is: function.apply(thisArg, [argsArray]), where thisArg represents the context object when the function is executed, that is The object pointed to by the this keyword in the function, argsArray is an array, representing the parameter list passed when the function is executed.

Implementation steps

Declare a function to be used later with the apply function.

function sum(a, b) {<!-- -->
  return a + b;
}

Then we now need to add the apply method to the function sum, which accepts two parameters: the context object thisArg and the argument array argsArray.

Function.prototype.apply = function(thisArg, argsArray) {<!-- -->
  // Code
};

In the apply function, the first parameter thisArg represents the context object to call the function. If no thisArg is passed, it defaults to the global object window.

Function.prototype.apply = function(thisArg, argsArray) {
  thisArg = thisArg || window;
};

We need to point the this keyword in the function to the context object thisArg when the function is executed, thereby changing the context object of the function.

Function.prototype.apply = function(thisArg, argsArray) {
  thisArg = thisArg || window;
  let fn = Symbol('fn');
  thisArg[fn] = this;
  let result = thisArg[fn](...argsArray);
  delete thisArg[fn];
  return result;
};

In the above code, we use a new Symbol variable fn to store the function object. Then we store the function object in the context object thisArg and immediately execute the function, thus changing the function’s context object. Finally, we delete the function object stored in the context object and return the result of the function execution.

The following is the code implementation of the complete apply function:

function sum(a, b) {
  return a + b;
}

Function.prototype.apply = function(thisArg, argsArray) {
  thisArg = thisArg || window;
  let fn = Symbol('fn');
  thisArg[fn] = this;
  let result = thisArg[fn](...argsArray);
  delete thisArg[fn];
  return result;
};

console.log(sum.apply(null, [1, 2])); // Output: 3

bind method

The bind function is also a function to modify the this point of the function, which is different from the immediate execution of the call and apply functions, bind The code> function returns a new function, which can be passed in parameters and executed in subsequent calls. Implementations of the bind function can save the passed in this value by using a closure in the returned new function, and return a new function.

The syntax of the bind function is as follows:

function.bind(thisArg[, arg1[, arg2[, ...]]])

Among them, thisArg is the value of this that needs to be bound, and arg1 , arg2 etc. are the parameters of the new function. The bind function returns a new function whose this value is bound to the incoming thisArg value, and additional parameters as arguments to the new function. When the returned new function is called, the original function will be called with the passed parameters and the previously bound this value as parameters.

Here is a manually implemented bind function:

Function.prototype.bind2 = function(thisArg) {<!-- -->
  var self = this;
  var args = Array.prototype.slice.call(arguments, 1);
  return function() {<!-- -->
    var bindArgs = Array.prototype.slice.call(arguments);
    return self.apply(thisArg, args.concat(bindArgs));
  };
}

In the implementation, save the pointer of this first, and then put the incoming parameters into the args array. Then return a new function, this new function will save the this value in the bind2 function and the parameters in the args array. When the new function is called, it will combine the previously saved this value and the parameters in the args array with the newly passed parameters into one array, and call the original function The apply method passes in this array as a parameter to achieve the purpose of modifying the value of this and passing in parameters.

Summary

In this article, we introduced the functions and implementation principles of the three functions call, apply and bind, and manually simulated and implemented these three functions . During the implementation process, we learned the this pointer of the function, the apply and call methods of the function, and the currying of the function.

Mastering these knowledge points will help us better understand the function mechanism in JavaScript, and be able to write more flexible and efficient code.

Start the growth journey of Nuggets! This is the 7th day of my participation in the “Nuggets Daily New Project · February Update Challenge”, click to view the event details