JS, as a weakly typed language, has many ways to judge types, and there are different ways to judge different types of data. This paper summarizes which scenarios apply which ways to judge.

JSThe data type

Before saying to judge the type, first summarize what data types JS has, generally divided into basic data types and reference data types.

Basic data types: NULL, undefined, String, number, Boolean, symbol, bigINT

Reference data types: Object, array, function

To be exact, JS has eight built-in types, namely seven basic data types and one reference data type object, such as Array, Date… These are called built-in objects.

Note: types begin with a lowercase letter, and constructors begin with an uppercase letter!!

typeof

Typeof Determines the basic data type

In general, typeof is used to determine basic data types:

let unde = undefined
typeof unde // "undefined"
typeof Madman // "undefined"

let str = 'Madman'
typeof str // "string"

let num = 1
typeof num // "number"

let boole = true
typeof boole // "boolean"

let sym = Symbol('id')
typeof sym // "symbol"

let bigi = 10n
typeof bigi // "bigint"
Copy the code

typeof null

Null = null; null = null;

let nu = null
typeof nu // "object"
Copy the code

why ?

There are books that say,

Null points to a pointer to an empty object, so Typeof judgment returns Object. “JavaScript Advanced Programming (Version 2)”

There are books that say,

In javascript, different objects are represented as binary objects at the bottom level. In javascript, the first three bits of the binary are considered as object objects, and the first three bits of the binary are all zeros, so the first three bits of the binary are also zeros. Therefore, ‘Object’ is returned when typeof is executed. Javascript you Don’t Know (Volume 1)

So you decide for yourself, and I’m not going to argue about it here, but how do you determine null?

let nu = null
nu === null // true
Copy the code

typeof number

In addition to null, there is another non-general, which is number. Although the above typeof num does return “number”, the following code also shows that it is non-generic.

let num = 1
typeof num // "number"

let na = NaN
typeof na // "number"
Copy the code

In JS NaN is a number itself, which is an abbreviation of Not a number. For example, NaN is returned when unexpected problems occur with the operator, such as:

2 * 'Madman'  // NaN

typeof 5*1 // NaN 
Copy the code

Because typeof takes precedence over the operator, the above is equivalent to (typeof 5) * 1;

typeofWhy can’t I determine the reference data type?

The first three digits of a null binary are all zeros. Therefore, if you use it to judge an object or an array, you will return an object. We have any good explanation can also share, here do not go too far.

typeof fnWhy returnfunction?

Function (fn === ‘function’); function (fn === ‘function’);

Function is actually a “subtype” of Object. Specifically, functions are “callable objects.” — JavaScript volumes you don’t know

Well, again, the book says that =v=, so since it is defined as a callable object, it must be different from the object. If you look up the relevant information, you will find that,

Function is implemented according to the ECMA-262 specification [[Call]],

Does typeof return ‘function’ if it implements [[call]] internally? This argument here is no longer in-depth, for a browser has not studied the implementation principle of the porter, this is just my point of view, we have what good views can also be published, learn from each other.

Lowercase are types, uppercase are constructors. Make sure you understand that.

Typeof Number typeof String Typeof function Typeof Array Typeof Object typeof class A {} // Typeof Null typeof Undefined /** * returns' Undefined '. * null and undefined do not have constructors */Copy the code

Number. The isNaN (), isNaN ()

typeof + Number.isNaN()Judge number types accurately

As mentioned above, Typeof cannot accurately judge whether a variable is a number type in our cognition, so now we can implement a number type that can accurately judge the number type in cognition through other ways.

function _isNum(num) { return typeof num === 'number' && ! Number.isNaN(num) }Copy the code

Number.isnan () : Used to determine if the value passed is a NaN and to check if it is of type Number

IsNaN () : is also used to determine whether a value isNaN. If the value passed is not of number type, number () is used to enforce this judgment. To try implementing a polyfill:

var isNaN = isNaN || function (v) { var n = Number(v) return n ! == n }Copy the code

One feature of NaN used above is that it is never equal to itself.

Implement a number.isnan () with isNaN() :

Number.isNaN = Number.isNaN || function(v) {
    return typeof v === 'number' && isNaN(v)
}
Copy the code

instanceof

Used to determine whether the right-hand constructor exists on the prototype chain of the left-hand object instanceprototype

The principle of

let date = new Date()
date instanceof Date // true
date instanceof Object // true

let fn = () => {}
fn instanceof Function // true
fn instanceof Object // true

let arr = []
arr instanceof Array // true
arr instanceof Object // true

let o = {}
o instanceof Object // true

class A {}
let a = new A()
a instanceof A // true
a instanceof Object // true
class A_1 extends A {}
let a_1 = new A_1()
a_1 instanceof A_1 // true
a_1 instanceof A // true
a_1 instanceof Object // true
Copy the code

Using the above code to summarize the rules, let’s implement a simplified version of Instanceof:

Object.prototype._instanceof = function (constru) {
    let consProto = constru.prototype
    let currentProto = this.__proto__
    
    while(true) {
        if (currentProto === null) return false
        if (currentProto === consProto) return true
        currentProto = currentProto.__proto__
    }
}
Copy the code

Note:Object.prototype.__proto__ Although supported by most browsers, and this property has beenES6This property is standardized in the specification, but it is still not recommended in the specification, so we can use itObject.getPrototypeOf()Instead of it.

whyinstanceofCan’t tell the basic type?

Take a look at the following code:

let n  = 2
n instanceof Number  // false
n instanceof Object // false

let n_n = new Number(2)
n_n instanceof Number // true
n_n instanceof Object // true

let str = 'Madman'
str instanceof String // false
str instanceof Object // false

let str_str = new String('Madman')
str_str instanceof String // true
str_str instanceof Object // true
Copy the code

We know that there are two common ways to create variables: a literal and a constructor.

Variables that are declared directly by literals are called “primitive values.” Primitive values have no properties or methods and are boxed only when they are used. Therefore, instanceof is generally not used to determine this basic data type.

constructor

This property is mounted to Object.prototype and returns a reference to the constructor that created the instance Object.

let n = 2 n.constructor === Number // true let str = 'Madman' str.constructor === String // true let date = new Date() date.constructor === Date // true let fn = () => {} fn.constructor === Function // true let arr = [] arr.constructor ===  Array // true let o = {} o.constructor === Object // true class A = {} let a = new A() a.constructor === A // true class A_1 extends A {} let a_1 = new A_1() a_1.constructor === A_1 // true a_1.constructor === A // falseCopy the code

That sounds nice, but the biggest problem with this approach is that the constructor property of the string, number, and Boolean types is read-only, and the constructor property of any other type can be modified!! .

Let date = new date () date.constructor = () => {console.log(' I have been modified ')} date.constructor === date // falseCopy the code

Of course, no one usually has time to change it.

Array.isArray()

This function is used to determine whether an object is an array.

let arr = []
Array.isArray(arr) // true
Copy the code

When determining whether an object is an Array, use array. isArray rather than instanceof.

becauseinstanceofandconstructorBoth are limited to top-level objects, while the former is not.

For example, if one page loads another page via iframe, the constructors of the two pages are not the same reference, because they belong to two top-level objects.

Although it’s not normally used that way.

Object.prototype.toString.call()

Each of the above judgments actually has its own advantages and disadvantages. We can judge each type in a relatively perfect way.

Object.prototype._isType = function(constructorStr) {
    return Object.prototype.toString.call(this) === `[object ${constructorStr}]`
}
Copy the code