Call, apply, and bind all work together to bind this to the function. For this, the dirty book (JavaScript You Don’t Know) describes four different ways to bind this.

  1. The default binding
  2. Implicit binding
  3. According to the binding
  4. The new binding

These three functions are in the third case.

Let’s look at the binding of this when the first argument to a native call is not an object

function test(){
    console.log('this'.this)
}
test.call() //this Window
test.call(undefined) //this Window
test.call(null) //this Window
test.call(123) //this Number{123}
test.call('abc') //this String{"abc"}
test.call(true) //this Boolean{true}
Copy the code

If the first parameter is undefined or null, this points to the global object, which is the window object in the browser environment. The first parameter is automatically boxed into an object when passed to another primitive type (primitive type). This is important.

The call, the apply

To convert a base type to the corresponding reference type, its constructor is used

const a = 1;
const aObj = new a.constructor(a);
console.log(aObj); // Number{1}
Copy the code

The same is true for other basic types.

The call function is implemented as follows.

Function.prototype.myCall = function(context, ... args){
    if(typeof this! = ='function') {throw Error('caller must be a function! ')}// The this context to bind
    let _context = context;
    // Pass this as undefined or null, and the context to bind this points to the global object
    if(_context === undefined || _context === null){
    	_context = globalThis;
    }
    // When this is passed as a primitive type, it is converted to the corresponding reference type
    if(typeof_context ! = ='object'){
      _context = new _context.constructor(context)
    }
    _context.func = this;
    // Call the function and bind this
    constres = _context.func(... args);delete _context.func;
    // Returns the execution result
    return res;	
}
Copy the code

Apply and Call are similar, except for the way arguments are passed

func.call(obj,arg1,agr2,...) ; func.apply(obj,[arg1,arg2,...] )Copy the code

The apply function is implemented as follows.

Function.prototype.myApply = function(context, args){
    if(typeof this! = ='function') {throw Error('caller must be a function! ')}// The this context to bind
    let _context = context;
    // Pass this as undefined or null, and the context to bind this points to the global object
    if(_context === undefined || _context === null){
    	_context = globalThis;
    }
    // When this is passed as a primitive type, it is converted to the corresponding reference type
    if(typeof_context ! = ='object'){
      _context = new _context.constructor(context)
    }
    let _args = args;
    if(_args === undefined || _args === null){
        _args = []
    } else if(!Array.isArray(args)){
        throw new TypeError("CreateListFromArrayLike called on non-object")
    }
    _context.func = this;
    // Call the function and bind this
    constres = _context.func(... _args);delete _context.func;
    // Returns the execution result
    return res;	
}
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

Function.prototype.myBind = function (context,... args) {
  if (typeof this! = ='function') {
      throw new TypeError('caller must be a function! ');
  }
  
  // The function to bind
  let toBindFunc = this
  // Return a function
  return function boundFunction() {
    // Because a function is returned, it may be called via new
    if (this instanceof boundFunction) {
      // The passed argument overrides the default argument
      return newtoBindFunc(... args, ... arguments) }// The passed argument overrides the default argument
    returntoBindFunc.apply(_context, args.concat(... arguments)) } }Copy the code