This is the 8th day of my participation in the August Text Challenge.More challenges in August

Write a call | apply | bind

01. call

  • call(thisArg, arg1, arg2)

    • The first parameter is the one you want to changethisPoint to the
    • The second argument takes a list of arguments
    let myCall = function(context){
        // 1. Get the this pointer that needs to be changed
        // If so, the specified object; if not, window
        // (Of course, this is not exact)
        context = context || window
        
        // 2. Get target function
        // The target function here is this
        // Because myCall is called as a method, for example [].slice.mycall ()
        // Therefore, inside the function this of course refers to the calling object, which is the target function f slice().
        // So we make the target function a member method of the context object
        //
        context.fn = this
        
        // 3. Obtain parameters
        let args = [...arguments].slice(1)
        
        // 4. Call this function to change the context to which this refers
        letresult = context.fn(... args)// delete this function
        delete context.fn
        
        // 6. Return the result
        return result
    }
    Copy the code

    For the context = context | | window

    • This with null and undefined will automatically point to the global object (window in browsers)

    • This with a primitive value (number, string, Boolean) refers to an instance object of that primitive value

    • That is, the Object (context)

    if (context === null || context === undefined) {
        context = window 
    } else {
        context = Object(context)
    }
    
    Copy the code

    For context.fn = this

    • fnMay conflict with the original property of the following object
    • You can useSymbolTo avoid this conflict
    const fn = Symbol('myCall')
    context[fn] = this;
    
    Copy the code

02. apply

  • apply(thisArg, [arg1,arg2])

    • The first parameter is the this pointer that needs to be changed

    • The second argument takes an Array of arguments (apply takes an Array Array, both beginning with A)

      This is implemented in the same way as call, but with different arguments

    let myApply = function(context){
        let context = context || window
        context.fn = this
        let result
        // Check if the second argument is an array
        if(arguments[1]){ result = context.fn(... arguments[1])}else {
            result = context.fn()
        }
        delete context.fn
        return result
    }
    Copy the code

    Note: There are a lot of details that are not addressed, so here is a simplified version of apply, as above


03. bind

  • bind(thisArg)

    • The first parameter is the one you want to changethis
    • This method returns a function directly
  • Simple version of the

    Function.prototype.myBind = function (context) {
        // Check if this is a function
        if (typeof this! ='function') {
            throw new TypeError('Error')}// Save this
        let that = this
        
        // Save the parameters
        let args = Array.prototype.slice.call([...arguments])
        
        // Define a function to return later
        let returnFunction = function () {
            // Return the function that changed this
            // The context above is the function called after changing this
            // Also pass in all the parameters
            return that.apply(context, [...args, ...arguments])
        }
        
        // Return this function
        return returnFunction
    }
    Copy the code
  • specifically

    The function returned by bind, which can also be a constructor

    So there’s a judgment to be made here, because the constructor’s this is not modified in any way, it always points to the instance object it creates

    Function.prototype.myBind = function (context) {
        // Check if this is a function
        if (typeof this! ='function') {
            throw new TypeError('Error')}// Save this
        let that = this
        
        // Save the parameters
        let args = Array.prototype.slice.call([...arguments])
        
        // Define a function to return later
        let returnFunction = function () {
            // When used as a constructor, this refers to the instance, i.e. there is no need to change the this reference
            // When used as a normal function, this refers to context
            // This is the this that calls it, not the this of the outer myBind
            if(this instanceof returnFunction){
                // This is an instance of returnFunction
                return that.apply(this, [...args, ...arguments])
            }else {
                return that.apply(context, [...args, ...arguments])
            }
        }
        
        // Then change the return function's prototype to the binding function's prototype, and the instance inherits the value from the binding function's prototype
        returnFunction.prototype = this.prototype
        
        // Return this function
        return returnFunction
    }
    
    Copy the code
  • After the optimization:

    Function.prototype.myBind = function (context) {
        // Check if this is a function
        if (typeof this! ='function') {
            throw new Error("It's not a function that's bound.")}// Save this
        let that = this
        
        // Save the parameters
        let args = Array.prototype.slice.call([...arguments])
        
        // Define a function to return later
        let returnFunction = function () {
            // When used as a constructor, this refers to the instance, i.e. there is no need to change the this reference
            // When used as a normal function, this refers to context
            // This is the this that calls it, not the this of the outer myBind
            return  that.apply(
                this instanceof returnFunction ? this : context,
                [...args, ...arguments]
            )
        }
        
        // Then change the prototype of the return function to the prototype of the instance of the binding function
        returnFunction.prototype =  Object.create(this.prototype)
        
        // Return this function
        return returnFunction
    }
    
    Copy the code

    Of course, there may still be some details that haven’t been considered… , hope understanding


I front-end side dish chicken, if there is wrong, please understand +