Detailed explanation and manual implementation of call, apply and bind in JavaScript functions

background

what is it

In the JS prototype chain learned earlier, we can know that all functions in js are instances of Function, and there are many properties and methods in the prototype object of Function (Function.prototype), including call, apply and bind methods .

According to the rules of the prototype, all functions can use the attributes and methods in the prototype, so all functions and only functions can use the call, apply and bind methods.

What are you doing?

Their function can be described in one sentence: it is to change the direction of this.

The difference between call(), apply() and bind()

same:

  • Both can change the internal this pointer when the target function is executed
  • The first parameter of the method is used to specify the internal value of this when the function is executed
  • Support passing any number of parameters to the target function
  • If no value is passed to the first parameter of the method or undefined and null are passed, then in JavaScript normal mode, this inside the target function points to the window object, and in strict mode, it points to undefined and null respectively.

different:

  • The apply() method can receive two parameters, while the call() and bind() methods can receive multiple parameters.
  • When the apply() method passes parameters to the target function, it only needs to use the parameter array or the arguments object as the second parameter of the method, while the call() and bind() methods need to list the passed parameters one by one after one parameter of the method .
  • When the call() and apply() methods are called, the target function will be executed immediately, but the bind() method will not, it will return a new function—a copy of the target function, and this inside the function points to the first One parameter, after which executing the new function is equivalent to executing the target function.
  • Only the bind() method implements function currying, so parameters can be passed to the target function twice.

call() and manual mock implementation

call() method

Calling the call() method will execute the target function immediately, and at the same time change the pointer of this inside the function. The this point is determined by the first parameter of the method, and any parameters listed one by one later will be passed in one by one as the parameters of the target function.

let obj = {
    
    
  name:"胡歌",
  sum(a, b) {
    
    
    console.log(this.name)
    return a + b
  }
}

let obj1 = {
    
    
  name:"李逍遥"
}
obj.sum.call(obj1, 1, 2)  //  李逍遥 3

/* 正常模式 */
obj.sum.call()  //  window
obj.sum.call(undefined, 1, 2)  //  window
obj.sum.call(null, 1, 2)  //  window
 
/* 严格模式 */
'use strict'
 
obj.sum.call()  //  undefined
obj.sum.call(undefined, 1, 2)  //  undefined
obj.sum.call(null, 1, 2)  //  null

Manual mock implementation

Design logic:

  • The myCall() method is added to the Function prototype object. When the target function calls this method, this inside the myCall() method will point to the target function.
  • Execute the target function as a method of the context object, so this inside the target function will point to the context object.
  • Remove the target function from the context object
  • Use the spread operator... to process arguments passed into the target function
Function.prototype.myCall = function (context, ...args) {
    
    
  if (context === undefined || context === null) {
    
    
    context = window
  }
  context.fn = this
  const result = context.fn(...args)
  delete context.fn
  return result
}
 


let obj1 = {
    
    
  num: 1,
  sum(a, b) {
    
    
    console.log(this)
    return this.num + a + b
  }
}
let obj2 = {
    
    
  num: 10
}
console.log(obj1.sum.call(obj2, 2, 3))  // 15
console.log(obj1.sum.myCall(obj2, 2, 3))  // 15

apply() and manual mock implementation

apply() method

Calling the apply() method will immediately execute the target function, and at the same time change the pointer of this inside the function. The this point is determined by the first parameter of the method, and the second parameter is a parameter array or an arguments object, and each parameter represented by each array element or the arguments object will be passed in one by one as the parameters of the target function.

obj1.sum.apply(obj2, [2, 3])

Manual mock implementation

Design logic:

  • The myApply() method is added to the Function prototype object. When the target function calls this method, this inside the myApply() method will point to the target function.
  • Execute the target function as a method of the context object, so this inside the target function will point to the context object.
  • Remove the target function from the context object
  • Use the spread operator... to process arguments passed into the target function
Function.prototype.myApply = function (context, args) {
    
    
  if (context === undefined || context === null) {
    
    
    context = window
  }
  // 下面这行为核心代码
  context.fn = this
  const result = context.fn(...args)
  delete context.fn
  return result
}
 
console.log(obj1.sum.apply(obj2, [2, 3]))  // 15
console.log(obj1.sum.myApply(obj2, [2, 3]))  // 15

bind() and manual mock implementation

bind() method

Calling the bind() method will also change the pointer of this inside the function, but instead of calling the function, it returns a new function—a copy of the target function. This inside the function points to the first parameter of the method, which will be listed one by one later Arbitrary parameters will be passed in one by one as the parameters of the objective function. Executing the new function afterwards is equivalent to executing the target function.

The bind() method implements function currying, so parameters can be passed to the target function twice. The first parameter is listed after the first parameter of the bind() method, and the second parameter is listed in the new function.

obj1.sum.bind(obj2, 2)(3)

Manual mock implementation

Design logic:

  • The myBind() method is added on the Function prototype object. When the target function calls this method, this inside the myBind() method will point to the target function.
  • Execute the target function as a method of the context object, so this inside the target function will point to the context object.
  • Remove the target function from the context object
  • Use the spread operator... to process the initial and subsequent arguments passed into the target function.
Function.prototype.myBind = function (context, ...initArgs) {
    
    
  if (context === undefined || context === null) {
    
    
    context = window
  }
  // 缓存 this 值
  const _this = this
  return function (...args) {
    
    
    // 下面这行为核心代码
    context.fn = _this
    const result = context.fn(...initArgs, ...args)
    delete context.fn
    return result
  }
}
 
console.log(obj1.sum.bind(obj2, 2)(3))  // 15
console.log(obj1.sum.myBind(obj2, 2)(3))  // 15

Guess you like

Origin blog.csdn.net/qq_44182284/article/details/121765202