call

The call() method calls a function with a specified this value and one or more arguments given separately.

grammar

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

parameter instructions
thisArg Optional. The this value used when function is run. Note that this may not be the actual value that the method sees: if the function is inNonstrict modeWhen specified as null or undefined, it is automatically replaced with a reference to the global object, and the original value is wrapped.
arg1, arg2, … Specifies the argument list.

thinking

var people={
  sex:'man'.age:27
}
function sayPeople(a,b){
  console.log(this.sex,this.age,a,b)
  return false
}
sayPeople.call(people,2.3) //man 27 2 3
Copy the code

The effect of the call implementation here is that external functions can call internal properties of the object (this refers to changes). It’s easy to understand: you can call an object’s internal properties by setting an external function as an object’s internal method.

var people={
  sex:'man'.age:27
}
function sayPeople(a,b){
  console.log(this.sex,this.age,a,b)
}
sayPeople.call(people,2.3)//man 27 2 3
// If you want to rewrite the code, you can find that the output is consistent
var people2={
  sex:'man'.age:27.sayPeople2:sayPeople2
}
function sayPeople2(a,b){
  console.log(this.sex,this.age,a,b)
}
people2.sayPeople2(4.5)//man 27 4 5
Copy the code

Analog implementation

The first step is to implement the _function_. Call (thisArg) form,

var people={
  sex:'man'.age:27
}
function sayPeople(){
  console.log(this.sex,this.age)
}

Function.prototype.newCall=function(obj) {
  // Set the calling function to the built-in function of obj
  obj.fn=this
  obj.fn()
  // Delete fn to avoid additional fn methods on the object
  delete obj.fn
}
sayPeople.call(people)    //man 27
sayPeople.newCall(people) //man 27
Copy the code

In addition to the _function_. Call (thisArg) form, we also need to consider _function_. Call (thisArg, arg1, arg2…). Key points:

  • Gets all parameters except the first one
  • How do I pass the obtained parameters into the obj.fn function

The first one is easy to solve by iterating over arguments. The second one is that we can’t pass the array of arguments directly to the fn function, so we can use eval(). The specific code is as follows:

The **eval() ** function executes the string passed as JavaScript code

var people = {
  sex: 'man'.age: 27
}
function sayPeople(a,b) {
  console.log(this.sex, this.age,a,b)
}

Function.prototype.newCall = function (obj) {
  // Set the calling function to the built-in function of obj
  obj.fn = this
  // Get all parameters except the first one
  var arr = []
  for (var i = 1; i < arguments.length; i++) {
    // Note that we are getting strings, not concrete values, because eval() is executing strings as code
    arr[i - 1] = 'arguments[' + i + '] '
  }
 // Use eval() to call fn methods with multiple arguments
  eval('obj.fn(' + arr + ') ')
  // Delete fn to avoid additional fn methods on the object
  delete obj.fn
}
sayPeople.call(people,1.2) //man 27 1 2
sayPeople.newCall(people,1.2)//man 27 1 2

Copy the code

Step 3 In addition, we need to consider two issues. The first is if the sayPeople function returns a return; The second is what happens if _thisArg_ is passed null or undefined. The first one is easy to handle as long as you return the result of calling the function. If null or undefined is passed in, point to window. The final result

var people = {
  sex: 'man'.age: 27
}
function sayPeople(a,b) {
  console.log(this.sex, this.age,a,b)
  return false
}

Function.prototype.newCall=function(obj) {
  // Check whether the object passed in is null or undefined, or if it points to window
  obj=obj || window  
  // Set the calling function to the built-in function of obj
  obj.fn=this
  // Get all parameters except the first one
  var arr=[]
  for(var i=1; i<arguments.length; i++) { arr[i-1] ='arguments['+i+'] '
  }
  // Use eval() to call fn methods with multiple arguments
  var result=eval('obj.fn('+arr+') ')
  // Delete fn to avoid additional fn methods on the object
  delete obj.fn
  // Consider calling the function sayPeople with a return
  return result
}

console.log(sayPeople.call(people,1.2)) 
//man 27 1 2
//false
console.log(sayPeople.newCall(people,1.2))
//man 27 1 2
//false

Copy the code

apply

The apply() method calls a function with a given this value and arguments in the form of an array (or array-like object). Note: Call () is similar to apply() except that call() accepts a list of parameters, while apply() accepts an array of parameters.

grammar

func.apply(thisArg, [argsArray])

parameter instructions
thisArg Will be selected. The this value used when the func function is run. Note that this may not be the actual value that the method sees: if the function is inNonstrict modeWhen specified as null or undefined, it is automatically replaced with a reference to the global object, and the original value is wrapped.
argsArray Optional. An array or array-like object whose array elements are passed as individual arguments to the func function. If the value of this parameter isnull 或 undefined”, no parameters need to be passed. Array-like objects can be used starting with ECMAScript 5.Browser compatibilitySee the bottom of this article.

thinking

The difference between Apply and Call is that the second parameter is implemented differently

var people = {
  sex: 'man'.age: 27
}
function sayPeople(. args) {
  console.log(this.sex, this.age,... args);return false
}
Function.prototype.newApply = function (obj, arr) {
  var result,args=[];
  // Check whether the object passed in is null or undefined, or if it points to window
  obj = obj || window
  // Set the calling function to the built-in function of obj
  obj.fn = this
  // Apply has only two parameters. If arr is null, execute fn
  if(! arr) { result = obj.fn() }else {
    for (var i = 0; i < arr.length; i++) {
      args[i] = 'arr[' + i + '] '
    }
    // Use eval() to call fn methods with multiple arguments
    result = eval('obj.fn(' + args + ') ')}// Delete fn to avoid additional fn methods on the object
  delete obj.fn
  // Consider the case where the function return is called
  return result
}
console.log(sayPeople.apply(people, [1.2.8.9.5]))
//man 27 1 2 8 9 5
//false
console.log(sayPeople.newApply(people, [1.2.8.9.5]))
//man 27 1 2 8 9 5
//false
Copy the code

bind

The bind() method creates a new function, and when bind() is called, this of the new function is specified as the first argument to bind(), and the remaining arguments will be used as arguments to the new function.

grammar

function_.bind(thisArg[, arg1[, arg2[, …]]])_

parameter instructions
thisArg The value passed to the target function as the this parameter when the binding function is called. If you are usingnewOperator to construct a binding function, the value is ignored. When using bind to create a function in setTimeout (provided as a callback), any raw values passed as thisArg are converted to Object. If bind’s argument list is empty, or thisArg is null or undefined, this executing the scope will be treated as thisArg for the new function.
arg1, arg2, Arguments that are preset into the argument list of the binding function when the target function is called.
The return value Returns a copy of the original function with the specified this value and initial arguments.

Analog implementation

Bind will return a copy of the original function :this refers to the same change principle, so we can get the following code from the above thinking

var people = {
  sex: 'man'.age: 27
}
function sayPeople(a,b) {
  console.log(this.sex, this.age,a,b)
  return false
}
Function.prototype.newBind=function(obj){
  // Save this in case this is lost
  var that = this
  var args=Array.prototype.slice.call(arguments.1)
  return function () {
   return  that.apply(obj, args)
  }
}
sayPeople.bind(people,2.2) ()//man 27 2 2
sayPeople.newBind(people,2.2) ()//man 27 2 2
Copy the code

We found that Bind also accepts arguments that return functions, so we need to merge the arguments

var people = {
  sex: 'man'.age: 27
}
function sayPeople(a,b) {
  console.log(this.sex, this.age,a,b)
  return false
}


Function.prototype.newBind=function(obj){
  // Prevent this from being lost
  var that = this
  var args=Array.prototype.slice.call(arguments.1)
  return function () {
     // Arguments refer to the arguments passed in by the function returned by bind
    // Note that arguments don't need to be split at this point, just convert the class array to an array
   return  that.apply(obj, args.concat(Array.prototype.slice.call(arguments)))
  }
}

sayPeople.bind(people,2) (2)//man 27 2 2
sayPeople.newBind(people,2) (2)//man 27 2 2
Copy the code

The second step is to create an instance of the bind return function using new.

var sex= 'woman';
var people = {
  sex: 'man'.age: 27
}
function sayPeople(a,b) {
  console.log(this.sex, this.age,a,b)
  return false
}
var sayPeople2 = sayPeople.bind(people,2)
var people2=new sayPeople2(2)
console.log(people2)
//undefined undefined 2 2
//sayPeople {}
Copy the code

A binding function can also use the new operator to create objects: this behavior is like treating the original function as a constructor. The supplied this value is ignored, and the arguments to the call are supplied to the mock function.



We can see that the “this” pointer inside sayPeople is missing. It does not point to people or window. But the parameters passed in are not affected. The reason for this is the use of new. So the solution is to modify the prototype and the first parameter passed into apply.

The key point

  1. When did you use new
  2. Change the return function’s prototype to point to sayPeople

thinking

  1. If new is used, the current this reference is an instance of the return function, so we can determine the use of new by whether the current this prototype is a return function
  2. Change the prototype by modifying the Prototype property

The specific implementation

var sex= 'woman';
var people = {
  sex: 'man'.age: 27
}
function sayPeople(a,b) {
  console.log(this.sex, this.age,a,b)
  return false
}
Function.prototype.newBind=function(obj){ 
  // Prevent this from being lost
  var that = this
  var args=Array.prototype.slice.call(arguments.1)
   function newFn () {
    // If this instanceof newFn is true, new is called
    return that.apply(this instanceof newFn ? this:obj, args.concat(Array.prototype.slice.call(arguments)))}// Change the return function's prototype to the binding function's (in this case sayPeople)prototype
  newFn.prototype=that.prototype
  return newFn
}
var sayPeople2 = sayPeople.newBind(people,2)
var people2=new sayPeople2(2)
console.log(people2)

Copy the code



Code optimization

If we change prototype in newFn, it will also affect prototype in the binding function sayPeople, so we need to use an empty function to mediate.

Function.prototype.newBind=function(obj){ 
  // If bind is not a function, an error is reported
  if (typeof this! = ="function") {
  throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
 }
  // Prevent this from being lost
  var that = this
  var emptyFn=function (){}
  var args=Array.prototype.slice.call(arguments.1)
   function newFn () {
    // If this instanceof newFn is true, new is called
    return that.apply(this instanceof newFn ? this:obj, args.concat(Array.prototype.slice.call(arguments)))}// Change the return function's prototype to the binding function's (in this case sayPeople)prototype
  emptyFn.prototype=that.prototype
  newFn.prototype=new emptyFn()
  return newFn
}
Copy the code

summary

  1. The key to understanding call/apply/bind is to understand call. Call: Call enables external functions to call internal properties of an object. So how can we use internal properties of objects — internal methods; So a new method is added to the object, which is the same as the external function. The method is deleted after the call, and the result is returned. This is just a simple understanding, please correct if it is not considered.
  2. The key to the apply implementation is the handling of the second argument (note that the second argument can be passed as an array or an array-like object)
  3. The key to implementing BIND is to understand new and stereotypes

The above is my learning ideas and learning ideas, if wrong, please help correct.