Project, we often use the Object directly. The prototype. The toString type used for judgment. It’s basically the best way to determine types out of the box.

Now let’s skin him.

Specific principles

When the toString method is called, the following steps ~ are performed

  1. To obtainThis points to theOf that object[[Class]]Property value.(This is why we use call to change the direction of this.)
  2. Evaluates the three strings “[object “, the Result of the first step, Result(1), and the new string concatenated with “]”.
  3. Return Result(2) of step 2, which is similar[object className]This format string.

[[Class]]Class attribute

A class attribute of an object is a string that represents the type information of the object. Neither ES3 nor ES5 provide a way to set this property, and there is only an indirect way to query it. The default toString method (inherited from Object.prototype) returns a string of the following form: [Object class] Therefore, to get the class of an Object, you can call the Object’s toString method and extract the characters from the eighth to the second to last position of the returned string.

Object.prototype.toString.call(target).slice(8, -1);
Copy the code

In summary, [[Class]] is a string value indicating the type of the object. It is an internal property that all objects (native and host) own and cannot be modified by anyone. In the specification, [[Class]] is defined as an internal attribute description.

The host object also contains meaningful “class attributes,” but this depends on the specific JavaScript implementation.

Because each type in JS has its own private [[Class]] property, and the Class cannot be modified by anyone, toString is the most accurate way to detect the property type, even better than Instanceof.

It can also subdivide class objects created by the built-in constructor:

Objects created through built-in constructors (Array, Date, and so on) contain “class attributes,” which match the names of the constructors (this is where I get the idea of distinguishing data types from contructor.name).

However, he cannot distinguish between custom object types.

Objects created by Object direct and object.create have class attributes of “Object”, as do objects created by custom constructors. Class attributes are “Object”, so for custom classes, there is no way to distinguish objects by class attributes.

Why usecall

Call is used to change the this reference inside toString, but you can also use apply.

We have to change this because inside toString we get the value of the [[Class]] property that this refers to. If we don’t change this to our target variable, this will always refer to the prototype that called toString. It is also because many objects inherit toString methods that are overridden, so call/apply is used indirectly in order to call the correct toString.

Code demo:

Object.prototype.toString = function ({
  console.log(this);
};
const arr1 = [];
Object.prototype.toString(arr1); // Prints object.prototype
Object.prototype.toString.call(arr1); // Prints (i.e. this points to) arr1
Copy the code

Why does null work? The primitive values of undefined and null have no attribute values.

Because each type has its own unique class attribute identifier, null and undefined also exist.

This method determines type defects

Although his judgment type is perfect, it is not without shortcomings, mainly including two points:

  1. The tostring will beThe operation using theProduce a lot ofTemporary objectsTherefore, it is recommended to cooperate with the actual type conversiontypeofTo distinguish between object types and primitive types, see last code)
  2. He can’t distinguishCustom object types, is used to determine this type of objectObject(This can be used for custom typesinstanceofDistinguish between)

Extension: What is a boxing operation?

  • "Packing"Basic types have the properties of objects wrapped in their corresponding reference types. It can be easily interpreted as“Packaging”.For more information, see JavaScript’s Definitive Guide -3.6 Wrapping Objects, which is not extended here.
  • Unboxing, on the other hand, reduces objects of reference type to data of value type

Encapsulate a complete tool for type determination

Although the toString method of the Object prototype is a relatively perfect scheme, in order to make up for the shortcomings of its packing, I encapsulated a tool function in my daily work, combined with the characteristics of Typeof and toString respectively, taking their strengths respectively, to judge the variable data type:

function classof(o{
  if (o === nullreturn "null";
  if (typeofo ! = ="object"return typeof o;
  else
    return Object.prototype.toString
      .call(o)
      .slice(8, -1)
      .toLocaleLowerCase();
}
Copy the code

Test type Verification result:

classof(2020); // number
classof("Rock on!"); // string
classof(true); // boolean
classof(undefined); // undefined
classof(null); // null
classof(Symbol("Nothing wrong!)); // symbol
classof(1n); // bigint
classof({}); // object
classof(classof); // function
classof([]); // array
classof(new Date()); // date
// There is still no way to subdivide the custom class, so encapsulate it with constructive.name if necessary
classof(new classof()); // object
Copy the code

More front-end problems, you can pay attention to the public number @ front-end imprint