background

As we all know, JS is a dynamic weak type scripting language, which uses dynamic type system and inheritance based on prototype. The lack of static constraints on types means that errors caused by data types cannot be found at compile time. In order to write robust code, various check& compatibility must be implemented at run time. Therefore, being able to detect data types skillfully and accurately becomes one of the most important foundations for mastering this language.

What are the tools for determining data types?

Generally speaking, there are roughly the following types: Typeof, instanceof, Object. The prototype. ToString, constructor, canard type, and for a specific type of Array detection method. The isArray (), Number. The isNaN (), although many methods, But their usage scenarios are different.

1. UsetypeofjudgeUnderlying data types:

The return value isUndefined, String, number, Boolean, object, function, symbolSeven.

As you can see,typeofAs the official type detection operator provided in detectionUndefined, string, Boolean, symbolThese basic data types andfunctionAspect is very reliable. The main reason for the performance breakdown

1) Object types (Array, Date, regExp) cannot be distinguished. Typeof null === 'object' // true...Copy the code

Typeof x === ‘object’ &&x! Typeof x === ‘object’ &&x! = = null. But not being able to distinguish between specific types of objects is a real pain point.

2. UseinstanceofDetermine the object data type



The operatorUsed to check if a constructor’s prototype appears on the target object’s prototype chain.

It’s a predictive test. It doesn’t look liketypeofInstead of returning the data type directly as a string, you need to anticipate the object type’s constructor and return a Boolean. Check whether an instance is created by a constructor. Now that you know the principle, you can implement an instanceof of of your own.

function myInstanceof(target,constructor){ const baseType = ['string', 'number',' Boolean ','undefined','symbol'] if(basetype.includes (typeof(target))) {return false} Let prototype = object.getProtoTypeof (target); While (prototype){// If (prototype){ Return true if(prototype === constructor. Prototype){return true}else{prototype = object.getProtoTypeof (prototype)}} return false } console.log(myInstanceof([],Array))Copy the code

In JS, you can think of it in a broad senseEverything comes from objects, for instance, although is through the constructor to create, but the constructor itself is not the feelings of production machine, the instance of soul and character (public properties and methods) are sharing the constructor of the prototype of a prototype property, and a prototype Object is pure Object, pure Object is founded by Object constructor, Then there will be the following result.



For arrays, iterating through objects on the prototype chain,Array.prototype Object.prototypeWill show up. Also, basic data types created literally cannot be determined. Such as



How do you make up for this? The answer is that you can use the following in a particular scenarioconstructorInstead of instanceof.

3. Usecontructorattribute

Let’s be clear.constructorIs a property on the stereotype that the instance inherits from, so it can be accessed directly on the instance as well. First of all to seecontructorThe universal performance of



It was a good surprise, exceptNull, and undefined, there arecontructorThe underlying (wrapper) type or object type of an attribute can be accurately determined.



Can tell the difference accuratelyArray|ObjectBecause it doesn’tinstanceofThat would go through the whole prototype chain, just judging on the instance. The fatal flaw, however, is that this property on the instance is too easily modified to make the method meaningless.

4. toStringmethods

First, there is either a js object type or a wrapper object for the underlying typetoStringMethods. Inherited fromObject.prototype.toString(), the call returns the string token “[Object Type]” of the corresponding Type.This method has a kind of disorderly boxing killed the old master, inadvertently inserted willow willow shade of feeling, the original role is just to get aA string representing the object, now used in JS type detection, performance is not too good, for the basic type and object type performance is very good, if not to say a disadvantage, can only say that the returned string is a bit complex, not too convenient to use, now let us start to simplify.

Let me write a simplified version

function isType(type,value){
    return Object.prototype.toString.call(value) === `[object ${type}]`
}
console.log(isType('Array',[]))
console.log(isType('Number',1))
Copy the code

It is not easy to use this method. It is easy to misspell type parameters such as’ Array ‘and’ Number ‘, so we want methods to have preset parameters, and we want to construct a function factory that returns functions like isArray. Function names have better code hints than strings in the IDE and are less likely to be misspelled.

function isType(type){
    return function(value){
        return Object.prototype.toString.call(value) === `[object ${type}]`
    }
}

const isArray = isType('Array')
const isNumber = isType('Number')
console.log(isArray([]),isNumber(1))
Copy the code

Bind can bind this to a new function. Bind can bind this to a new function. Bind can bind this to a new function.

function isType(type,value){
    return Object.prototype.toString.call(value) === `[object ${type}]`
}

const isArray = isType.bind(null,'Array')
const isNumber = isType.bind(null,'Number')
console.log(isArray([]),isNumber(1))
Copy the code

Further, transform a wave with the idea of parametric Currization

function isType(type,value){ return Object.prototype.toString.call(value) === `[object ${type}]` } function curring (fn,... args1){ let len = fn.length; return function(... args2){ const args = args1.concat(args2); if(args.length < len){ return curring(fn,... args) }else{ return fn(... args) } } } const isArray = curring(isType,'Array') const isNumber = curring(isType,'Number') console.log(isArray([]),isNumber(1))Copy the code

Finally, diversify the types of support, and you’re done.

const types = [
    'Null',
    'Undefined',
    'String',
    'Number',
    'Boolean',
    'Object',
    'Array',
    'Date',
    'Function',
    'RegExp',
    'Symbol',
    'Math',
]
const checkTypeUtil = {}
types.forEach((type)=>{
    checkTypeUtil[`is${type}`] = curring(isType,type)
})
export {
 checkTypeUtil
}
console.log(checkTypeUtil.isArray([]))
Copy the code

5. UseArray.isArrayJudge array

Array and array. prototype must be different in each window because the window global environment needs to be isolated. Iframea.array. prototype ≠ iframeb.array. prototype, iframea.arr instanceof iframeB.Array must return false. However, in iframe scenarios, it is also very possible to pass values to each other. IsArray does not have this problem and can accurately determine the Array. You can do that pollify

if (! Array.isArray) { Array.isArray = function(x) { return Object.prototype.toString.call(x) === '[object Array]'; }; }Copy the code

6. To distinguishArrayLikewithArray

The class array is defined as:

  • havelengthProperty, other attributes (indexes) are non-negative integers (indexes in objects are treated as strings)
  • Does not have the methods that arrays do
function isLikeArray(x){ if(! (typeof x === 'object' && x ! == null)){ return false } return typeof x.length === 'number' && x.length >= 0 && ! Array.isArray(x) }Copy the code

Class can use the Array. The Array from an Array. The prototype. Slice. The call (val) is converted into a true Array.

7. Determine if an object is pure (or plain)

Definition of a pure object: It refers to an object created in the following three ways

  • new Object
  • Object literal creation {}
  • Object.create(null)

Jquery, LoDash source code is to use the following method to detect

const funcToString = Function.prototype.toString const objectCtorString = funcToString.call(Object) function IsPlainObject (value){// Use toString to exclude other data types if(! value || ! Object.prototype.toString.call(value) === "[object Object]"){ return false } const proto = Object.getPrototypeOf(value) If (proto === null){// Compatible with Object.create(null) return true} const Ctor = Object.prototype.hasOwnProperty.call(proto,'constructor') && proto.constructor; if(typeof Ctor ! == 'function'){return false} If (functoString. call(Ctor) === objectCtorString){return true} return false} console.log(isPlainObject(Object.create(null))) console.log(isPlainObject(new Object)) console.log(isPlainObject({a:1}))Copy the code

8. NaNHow to detect,Number.isNaNwithisNaNWhat is the difference between



Conclusion: Number.isNaN strictly determines whether an incoming value is directly equal to NaN. IsNaN does the Number() conversion first, and then determines whether it is a NaN.

9. Duck type detection

In fact, constuctor is used to determine data types in this way. Whether or not an animal is a duck can be roughly determined by simple empirical judgments that it looks like a duck and sounds like a duck. For example, to determine if an object is a Promise, you can do this

function isPromise(x){ if(! (x instanceof Promise)){ return false } return typeof x.then === 'function' }Copy the code