This is the first article I participated in beginners’ introduction

Learn to write call and apply by hand

This article is mainly a summary of today’s handwritten call and apply learning and some problems encountered in the learning process…

Write a call

Suppose the call method is called on a func object with thisArg and optional arg1, arg2, etc. :

1. Determine whether the call func is a callable function. If not, TypeError is thrown. ThisArg is null or undefined. If so, replace thisArg with a global object. If not, apply Object() to it and convert it to an Object(whose content remains unchanged). 3. Bind func to thisArg and pass the arguments starting with arg1 to func 4 in left-to-right order. Gets and returns the result of the func execution after binding thisArg and passing the argument.

The code is as follows:

    Function.prototype.my_call = function (thisArg, ... args) {
      // if the caller is not a callable function, TypeError is raised
      if (typeof this! = ='function') {
         throw new TypeError(`The ${this} must be a function`)}// if the binding object is null or undefined, bind it to the global object (window in browser).
      if (thisArg == null) { // Type conversion
        thisArg = window
      } else {
        thisArg = Object(thisArg) // If it is a normal type, it is converted to an object wrapper
      }
      const fn = Symbol('Unique identifier')
      thisArg[fn] = this
      constres = thisArg[fn](... args)delete thisArg[fn] ThisArg = thisArg = thisArg = thisArg = thisArg = thisArg
      return res
    }
Copy the code

In learning and written the code exist the following problems: 1, why in judging thisArg in judging whether null or undefined cannot use thisArg = thisArg | | window this way? If this is used then this will point to window for thisArg 0, “”, false, NaN. In addition, the null-value merge operator?? It can be executed correctly. 2. What’s wrong with using the Symbol as a unique Symbol? You can replace it in other ways, right? What could be wrong with that? Symbol is the Symbol after ES6. (is this it? You don’t have hands? Hey, where’s my hand… If you can use Symbol, then you need to write a call? The substitution is as follows

.const fn = new Date().getTime() // Generate random numbers? !
    const originData = thisArg[fn] // Save the original data
    const hasProp = thisArg.hasOwnProperty(fn) // Whether the fn attribute exists
    thisArg[fn] = this
    constres = thisArg[fn](... args)// originData! ==undefined to determine whether the attribute exists?
    // If the original object has a undefined value attribute...
    if(hasProp) { // If fn attributes exist
        thisArg[fn] = originData // Change to the original property
    } else {
        delete thisArg[fn]
    }
    return res
Copy the code

3. What’s wrong with using rest and spread syntax when passing parameters? How do I replace it? Ah this! Okay, I’m compromised. This won’t be… 4. There is a special phenomenon:

    console.log('Original call test null type :'.Object.prototype.toString.call(null))
    console.log('Raw Call test undefined type :'.Object.prototype.toString.call(undefined))
    console.log('Handwriting call test null type :'.Object.prototype.toString.my_call(null))
    console.log('Handwriting call test undefined type :'.Object.prototype.toString.my_call(undefined))
Copy the code

The results are:



At that time I was confused, why is this? Could it be that null and undefined were not handled properly? I went to look againMDN.



Null and undefined are null and undefined. Bald, still do not find the reason to want, on the outrageous…

Had to compromise… If youdao friends know, but also please give advice, very grateful!

                                                  

Handwritten apply

Suppose the apply method is called on a func object with thisArg and argArray as arguments:

1. Determine if func is a callable function. If not, TypeError is thrown. ThisArg is null or undefined. If so, replace thisArg with a global object. If not, apply Object() to it and convert it to an Object(whose content remains unchanged). 3. Check whether argArray is null or undefined. If so, replace the original value of argArray with []. 4. Check whether argArray is of Object type. If not, TypeError is thrown. 5. Check whether argArray is an array-like object. If not, replace the original value of argArray with []. 6. Bind func to thisArg. Gets and returns the value of the argArray argument passed to the post-binding func execution

The code is as follows:

   function isArrayLike(val) {
      //val.length >= 0 excludes negative numbers and nans
      if (typeof val.length === 'number' && val.length >= 0) {
        if(!isFinite(val.length)) {
          throw new RangeError('Invalid array length')}return true
      }
      return false
    }
    Function.prototype.my_apply = function (thisArg, argArray) {
      // check whether func is a callable function
      if (typeof this! = ='function') {
        throw new TypeError(`The ${this} is not a function`)}//2、判断thisArg是否是null或者undefined
      if (thisArg == null) {
        thisArg = window // This is a global object. In the browser environment, this object is window
      } else {
        thisArg = Object(thisArg) // Others are converted to objects
      }
      //3、判断thisArg是否是null或者undefined
      if (argArray == null) {
        argArray = []
      }
      // check if argArray is an object
      if(! (argArrayinstanceof Object)) {
        throw new TypeError('CreateListFromArrayLike called on non-object')}// check if argArray is an array-like object
      const argList = []
      if (isArrayLike(argArray)) { // argArrar is null or undefined has been converted to an empty array
        // Argarray. length may not be an integer
        for (let i = 0; i < Math.floor(argArray.length); i++) {
          argList[i] = argArray[i]
        }
      }
      const fn = Symbol('Unique identifier')
      thisArg[fn] = this
      constres = thisArg[fn](... argList)delete thisArg[fn]
      return res
    }
Copy the code

There are several problems when learning Apply: 1. IsFinite ()

You can use this method to determine if a number is finite. The isFinite method checks the value of its parameters. Returns false if the argument is NaN, plus or minus infinity, and true otherwise.

That is, isFinite() does not determine whether its parameters are numbers. A parameter can only be determined as a finite number if it is determined in advance that the parameter is a number. So, in code isArrayLike(), isFinite() is placed in a conditional statement. 2. In step 5, after determining that it is an ArrayLike, you can use array.from () to convert it to an Array. 3. It is worth mentioning that the determination of ArrayLike used here seems to be different from the JS authoritative guide. I’ll mention it here, and I’ll look it up later when I get an authoritative guide

The last

I’m new, I don’t know enough about how to write a good article… In terms of handwriting call and apply, if you have different views, welcome to communicate with us. If you find any mistakes in this article, please let me know. Thank you very much. Come on! The front; Come on! The six ears.

reference

  • MDN
  • Interviewer: Can you simulate implementing JS call and apply methods
  • Js Basics – Interviewers want to know how well you understand call,apply,bind?