typeof

Commonly used to determine the type of a variable: Number string undefined Boolean function symbol bigint object Number string undefined Boolean function bigint object

Such as:

let ss = '2good'
typeof ss // string

let s = new String('2good')
typeof s // object

s instanceof String // true
Copy the code

Principle:

When js stores variables at the bottom level, it stores their type information in the lowest 1-3 bits of the variable’s machine code.

  • 000: object
  • 010: floating point number
  • 100: string
  • 110: Boolean
  • 1: the integer

However, in the case of undefined and NULL, the information storage for these two values is a bit special.

Null: all machine codes are 0, since null represents a null pointer (0x00 on most platforms), undefined: a -2^30 integer

Therefore, TypeOF has a problem with nulls, which are treated as objects because their machine code is zero.

However, judging by instanceof

null instanceof null // TypeError: Right-hand side of 'instanceof' is not an object
Copy the code

Due to the use of instanceof words. The left-hand side must be object, but this is directly judged not object, which is also a legacy of JavaScript bug

Therefore, when using typeof to determine variable types, we need to note that it is best to use Typeof to determine basic data types (including symbol) and avoid null. Because typeof NULL results in “object”.

typeof 1= = ='number' // true
typeof Infinity= = ='number'; // true
typeof NaN= = ='number'; // true, although it is short for "not-a-number"

typeof '123'= = ='string' // true
typeof (typeof 1) = = ='string'; // Typeof always returns a string
typeof String(1) = = ='string'; // String converts any value to a String, which is safer than toString

typeof null= = ='object' // true, note that null is not an object, this is special.

typeof [1.2.4= = ='object'; // true

typeof true= = ='boolean' // true
typeof function a() = = = {}'function'
typeof Symbol(2) = = ='symbol'
typeof undefined= = ='undefined'

// Use the new operator
// All constructors except Function are of type 'object'
typeof new Number(100) = = ='object'
typeof new String('123') = = ='object'
typeof new Date() = = ='object'
var func = new Function(a);typeof func === 'function'

//NaN
typeof Number("123z") = = ="number" // 123Z is a NaN when it encounters z errors while converting numbers. NaN is of type number
typeof 1/0; //NaN (this NaN is not a string type, but a numeric type)
typeof (typeof 1/0) = = ='number'; //NaN (this NaN is not a string type, but a numeric type)
Copy the code

instanceof

Instanceof is used to check whether the constructor’s Prototype property appears on the prototype chain on a sample object. MDN

let Person = function () {}
let xxx = new Person()
xxx instanceof Person // true
Copy the code

Note: The left-hand side must be an object.

let a = 1
a.__proto__ === Number.prototype // true
a instanceof Number // false

let b = new Number(1)
b.__proto__ === Number.prototype // true
b instanceof Number // true
Copy the code

Of course, instanceof can also determine whether an instance is an instanceof its parent or ancestor type.

let Person = function () {}
let Programmer = function () {}
Programmer.prototype = new Person()

let nicole = new Programmer()
nicole instanceof Person // true
nicole instanceof Programmer // true
Copy the code

Principle:

Instanceof iterates through the prototype chain of the left variable until it finds the prototype of the right variable and returns false if the lookup fails.

Implement instanceof

function myInstanceof(left, right) {
    let r = right.prototype
    let l = left.__proto__
    // If not, loop to the parent or ancestor type
    while(true) {
        if (l === null) {
            return false
        }
        if (l === r) {
            return true
        }
        l = l.__proto__ // Get the __proto__ of the ancestor type}}let P = function () {}
let a = new P()
let b = 2
myInstanceof(a, P) // true
myInstanceof(b, P) // false
Copy the code

Note: By modifying the __proto__ attribute of the object or the prototype attribute of the constructor, it is possible that the corresponding prototype in the prototype chain will not be found.

let P = function () {}
let P1 = function () {}
let a = new P()
a instanceof P // true
a.__proto__ = {}
a instanceof P // false
P.prototype / / {constructor: ƒ}
P.prtotype = P1.prototype // Change the prototype pointing of P
a instanceof P // false
Copy the code

There’s another way to look at it.

Symbol. HasInstance is used to determine whether an object is an instance of a constructor. So you can use it to customize the behavior of the Instanceof operator on a class.

class Array1 {
  static [Symbol.hasInstance](instance) {
    console.log('Executed this method', instance)
    return Array.isArray(instance)
  }
}
[] instanceof Array1 // true
({} instanceof Array1) // false
Copy the code

Above, we can use typeof to determine basic types, but note the problem of typeOF to determine null types. If you want to determine the specific type of an object can consider to use instanceof, but also see the judgment is not accurate, such as an array, he can be judged as object, so we want to more accurately judge the type of object instances, can take the object. The prototype. The toString method.

Object.prototype.toString()

The toString() method returns a string representing the object

The toString() method is inherited by each Object. If this method is not overridden in a custom object, toString() returns “[Object type]”, where type is the type of the object.

new Object().toString() // "[object Object]"
Copy the code

For example, Array and Date are not returned with the format “[object “+ tag + “]” because they have an internal custom return format for the object.

[].toString() / / ""
(new Date()).toString() // "Fri Apr 09 2021 09:51:07 GMT+0800 (GMT+08:00)"
Copy the code

Principle:

When the toString(O) method is called, the following steps are performed: tc39.es/

  1. If the this value is undefined, return "[object Undefined]".
  2. If the this value is null, return "[object Null]".
  3. Let O be ! ToObject(this value).
  4. Let isArray be ? IsArray(O).
  5. If isArray is true, let builtinTag be “Array”.
  6. Else if O has a [[ParameterMap]] internal slot, let builtinTag be "Arguments".
  7. Else if O has a [[Call]] internal method, let builtinTag be "Function".
  8. Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error".
  9. Else if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean".
  10. Else if O has a [[NumberData]] internal slot, let builtinTag be "Number".
  11. Else if O has a [[StringData]] internal slot, let builtinTag be "String".
  12. Else if O has a [[DateValue]] internal slot, let builtinTag be "Date".
  13. Else if O has a [[RegExpMatcher]] internal slot, let builtinTag be "RegExp".
  14. Else, let builtinTag be "Object".
  15. Let tag be ? Get(O, @@toStringTag).
  16. If Type(tag) is not String, set tag to builtinTag.
  17. Return the string-concatenation of "[object ", tag, and "]".
// The following is the pseudo-code for easy comprehension. If there are deficiencies, but also hope to correct.

// Wrap the object
function ToObject(O) {
    if (O === undefined || O === null) {
        throw new Error('Throw a TypeError exception')}let res = null;
    switch(typeof O) {
        case 'Boolean': res = new Boolean(O); break;
        case 'Number': res = new Number(O); break;
        case 'String': res = new String(O); break;
        case 'Symbol': res = new Object(Symbol(O)); break;// Note that Symbol here does not support the new syntax
        case 'BigInt': res = new Object(BigInt(O)); break;
        default: res = O; break;
    }
    return res;
}
// Check whether it is an array
function IsArray (O) {
    return Array.isArray(O)
}
// toString internal execution step
function toString(O) {
    let bulitinTag = ' '
    if (O === undefined || O === null) {
        return `[object ${ O }] `
    }
    O = ToObject(O)
    if (IsArray(O)) {
        bulitinTag = "Array"
    }
	
    let slotName =  O[[class]]; // This is the internal slot class of the object
	
    switch(slotName) {
        case '[[ParameterMap]]': bulitinTag = "Arguments"; break;
        case '[[Call]]': bulitinTag = "Function"; break;
        case '[[ErrorData]]': bulitinTag = "Error"; break;
        case '[[BooleanData]]': bulitinTag = "Boolean"; break;
        case '[[NumberData]]': bulitinTag = "Number"; break;
        case '[[StringData]]': bulitinTag = "String"; break;
        case '[[DateValue]]': bulitinTag = "Date"; break;
        case '[[RegExpMatcher]]': bulitinTag = "RegExp"; break;
        default:  bulitinTag = "Object"; break;
    }
    let tag = O[Symbol.toStringTag] // Get the object's custom [symbol.toStringTag] method.
    if (typeoftag ! = ='String') {
        tag = bulitinTag
    }
    return `[object ${ tag }] `
}
Copy the code

Here is an example of customizing symbol.toStringTag. As you can see, the attribute value is expected to be a string, otherwise it will be ignored.

var test = {
  [Symbol.toStringTag]: "Good"
}
test.toString() // "[object Good]"

var test1 = {
  [Symbol.toStringTag]: 2
}
test1.toString() // "[object Object]"
Copy the code

Symbol.tostringtag can also be deployed on the prototype chain:

class A {}
A.prototype[Symbol.toStringTag] = "A";
new A().toString()   // "[object A]"
Object.prototype.toString.call(new A) // "[object A]"
Copy the code

To every Object through the Object. The prototype. The toString () to test, need to Function. The prototype. The call () or the Function. The prototype, the apply () in the form of a call, Pass the object to be checked as the first argument, called thisArg.

// Arguments type, tag = "Arguments"
Object.prototype.toString.call((function() {
  return arguments; }) ());// => "[object Arguments]"

// Function type, tag is "Function"
Object.prototype.toString.call(function(){});    // => "[object Function]"

// Error type (including subtypes), tag "Error"
Object.prototype.toString.call(new Error());     // => "[object Error]"

// RegExp type, tag "RegExp"
Object.prototype.toString.call(/\d+/);           // => "[object RegExp]"

// Date type, tag is "Date"
Object.prototype.toString.call(new Date());      // => "[object Date]"

// Other type, tag is "Object"
Object.prototype.toString.call(new class {});    // => "[object Object]"
Copy the code

The new standard introduces the [symbol.toStringTag] attribute to interface the method and to regulate calls to the method by newly introduced objects. But for “old” objects, you can only output values directly to ensure compatibility.

conclusion

  1. Basic data types can be identified using typeof, note thattypeof nullIs the case of “object”.
  2. Instanceof can only be used to determine reference data types, not base data types. The downside is that if the prototype chain points to changes, this judgment can be wrong.
  3. Object. The prototype. ToString suited to judge any type data.

Finally, thank you for reading this article, I hope you can help, if you have any questions welcome to point out.

Reference:

Juejin. Cn/post / 684490…