This article introduces the use of call, Apply, bind and their respective implementation principles.

call

The call() method calls a function with a specified value of this and one or more arguments given separately. You can change the point of this to the current function. It also lets the current function execute.

usage

function fun() {
  console.log(this.name, arguments)}let obj = { name: 'clying' }
fun.call(obj, 'deng'.'deng')
// clying [Arguments] { '0': 'deng', '1': 'deng' }
Copy the code

implementation

Both call and Apply are implemented by placing the function in a property of the obj literal so that this in the function refers to the obj literal.

Simple implementation version:

Function.prototype.mycall = function (context) {
  context = (context == null || context == undefined)?window : new Object(context)
  context.fn = this
  context.fn()
  delete context.fn
}
Copy the code

Add the myCall method to the function prototype and create a context object that will point to the global Window if the object passed in does not exist. By adding the fn attribute to the context, the fn reference to the context calls the function fun of that method and executes fun. Delete the fn property when the execution is complete.

We need to get the parameters passed in first, so it becomes an array of strings. The execution method uses the eval function to evaluate the string and then executes the code in it, returning the result of the evaluation.

The upgrade version:

Pass parameters to the call.

Function.prototype.mycall = function (context) {
  context = (context == null || context == undefined)?window : new Object(context)
  context.fn = this
  let arr = []
  for (let i = 1; i < arguments.length; i++) {
    arr.push('argument[' + i + '] ') // ["arguments[1]", "arguments[2]"]
  }
  let r = eval('context.fn(' + arr + ') ') // Execute the function fun, passing in the arguments
  delete context.fn
  return r
}
Copy the code

In addition, call can also be implemented through deconstructed syntax.

Function.prototype.mycall = function (context, ... args) {
  context = (context == null || context == undefined)?window : new Object(context)
  context.fn = thiscontext.fn(... args)delete context.fn
}
Copy the code

If you want to be able to call the call method multiple times, you can use context.fn(… Args) in the variable, and then return it.

Function.prototype.mycall = function (context, ... args) {
  context = (context == null || context == undefined)?window : new Object(context)
  context.fn = this
  letr = context.fn(... args)delete context.fn
  return r
}
Copy the code

apply

Similar to the call method, the call method receives a list of arguments, while the Apply method receives an array of arguments.

usage

Point this in the function to the first argument passed in, and the second argument is an array.

function fun() {
  console.log(this.name, arguments);
}
let obj = {
  name: 'clying'
}
fun.apply(obj, [22.1])
// clying Arguments(2) [22, 1]
Copy the code

implementation

Implement your own apply method myapply. The implementation is similar to call, except that you can use an args as the second parameter passed in when the parameter is received. If no second argument is passed, execute the function directly; Otherwise, use eval to execute the function.

Function.prototype.myapply = function (context, args) {
 context = (context == null || context == undefined)?window : new Object(context)
  context.fn = this
  if(! args)return context.fn()
  let r = eval('context.fn('+args+') ')
  delete context.fn
  return r
}
Copy the code

bind

The bind() method creates a new function, which is not automatically executed and requires a manual call to bind(). This to the new function is specified as the first argument to bind(), and the rest of the arguments to the new function will be used when called.

usage

Bind obj to this of fun. Fun can use the properties inside obj, and the variables passed in.

function fun() {
  console.log(this.name, arguments);
}
let obj = {
  name: 'clying'
}
let b = fun.bind(obj,2)
b(3)
// clying Arguments(2) [2, 3]
Copy the code

In addition, the function to which bind is bound can also be used to create a new instance, but this will change.

Updated version – Using stereotype properties

function fun() {
  console.log(this.name, arguments);
}
let obj = {
  name: 'clying'
}
fun.prototype.age = 23
let b = fun.bind(obj, 3)
let instance = new b(4)
console.log(instance.age);
//undefined Arguments(2) [3, 4]
/ / 23
Copy the code

implementation

The basic version:

The implementation of bind can be implemented on the basis of call and Apply.

Because bind does not execute immediately, you can let the user execute it manually by returning a function. Pass the specified this object and parameters in the return function using call or apply.

Apply to implement the bind

Function.prototype.mybind = function (context) {
  let that = this
  let bindargs = Array.prototype.slice.call(arguments.1)
  return function () {
    let args = Array.prototype.slice.call(arguments)
    return that.apply(context, bindargs.concat(args))
  }
}
Copy the code

The apply method is used to retrieve the arguments passed in to bind and the arguments passed in to the user-executed function. The required parameters are truncated using the slice method of the Array prototype method. To get the arguments passed to bind, you need to start with the second argument, so the starting position is 1.

The call to realize the bind

Function.prototype.mybind = function (context, ... args1) {
  let that = this
  return function (. args2) {
    returnthat.call(context, ... args1, ... args2) } }Copy the code

The call implementation simply concatenates the parameters directly after the call method.

The upgrade version:

In addition to the fact that bind can change the point to this and that the user can pass arguments after bind or at the time of execution. You can also have the execution function do new.

When a binding function is used to construct a value, the originally provided this is ignored. However, the supplied argument list is still inserted before the argument list in the constructor call.

apply

Function.prototype.mybind = function (context) {
  let that = this
  let bindargs = Array.prototype.slice.call(arguments.1)
  function fBind() {
    let args = Array.prototype.slice.call(arguments)
    // If new is used, then this points to the fBind instance. If this is passed as the current instance, then the context object is used
    return that.apply(this instanceof fBind ? this : context, bindargs.concat(args))
  }
  return fBind
}
Copy the code

When using the new operator, note that you need to change the reference to this. If this is new, then this refers to the instance. If not, this refers to the first argument currently passed to bind.

In addition, the function can add its own method attributes. If you want to be able to use fun’s own prototype methods, you need to use fbind. prototype = this.prototype to share prototypes. However, for reference type attribute values to be shared, they cannot be changed without changing other instances (if one stereotype method or attribute changes, all referenced ones will change).

Function.prototype.mybind = function (context) {
  let that = this
  let args = Array.prototype.slice.call(arguments.1)
  function fBind() { // Execute the bind function
    let bindargs = Array.prototype.slice.call(arguments)
    return that.apply(this instanceof fBind ? this : context, args.concat(bindargs))
  }
  function Fn(){} // The prototype of the two classes is not common, but the prototype method is found through the prototype chain
  Fn.prototype = this.prototype
  fBind.prototype = new Fn()
  return fBind
}
Copy the code

In this case, a prototype chain can be used in the form of a function middleware to find the original function prototype method or property.

call

The difference between Call and Apply is that the parameters are handled differently, and everything else is similar.

Function.prototype.mybind = function (context, ... args1) {
  let that = this
  function fBind(. args2) {
    return that.call(this instanceof fBind ? this: context, ... args1, ... args2) }function Fn() { }
  Fn.prototype = this.prototype
  fBind.prototype = new Fn()
  return fBind
}
Copy the code