Typeof implementation principle

Typeof is used to determine the typeof a variable. We can use typeof to determine the typeof a variable, number, string, object, Boolean, function, undefined, and symbol. This judgment can help us solve some problems. For example, when determining data that is not of object type, Typeof can tell us clearly which type it is. However, it is a pity that typeof can only tell us that the data is object when judging the data of an object, rather than which object it is, such as 👉

let s = new String('abc');
typeof s === 'object'// true
s instanceof String // true
Copy the code

To determine what kind of object a piece of data is, we need to use the instanceof operator, which we will talk about later.

To talk about the principle of typeof, we can first think of an interesting question: how does JS store data type information at the bottom? In other words, how is the type information of a JS variable implemented in its underlying implementation?

In fact, when JS stores variables at the bottom level, it will store their type information 👉 in the low 1-3 bits of the variable’s machine code

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

But, for undefined and NULL, the information storage for these two values is a bit special.

Null: All machine codes are 0

Undefined: it is an integer of −2^30

As a result, Typeof has a problem judging null, because null is treated as an object because all machine code is zero.

However, judging by instanceof 👉

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

Null is not an object. This is also a JavaScript bug. Refer to Typeof.

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.

There is also a good type of method, is the Object. The prototype. ToString, we can use this method to a variable of type to compare accurate judgment

Object.prototype.toString.call(1) // "[object Number]"

Object.prototype.toString.call('hi') // "[object String]"

Object.prototype.toString.call({a:'hi'}) // "[object Object]"

Object.prototype.toString.call([1.'a']) // "[object Array]"

Object.prototype.toString.call(true) // "[object Boolean]"

Object.prototype.toString.call((a)= > {}) // "[object Function]"

Object.prototype.toString.call(null) // "[object Null]"

Object.prototype.toString.call(undefined) // "[object Undefined]"

Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
Copy the code

How the instanceof operator works

Before, we mentioned instanceof to determine the specific type of objects. In fact, the main function of instanceof is to determine whether an instance belongs to a certain type

let person = function () {}let nicole = new person()
nicole instanceof person // 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

That’s instanceof, but how does instanceof work? According to the ECMAScript language specification, I combed through the general idea and then compiled a code as follows

function new_instance_of(leftVaule, rightVaule) { 
    let rightProto = rightVaule.prototype; // Take the prototype value of the right expression
    leftVaule = leftVaule.__proto__; // Take the __proto__ value of the left expression
    while (true) {
    	if (leftVaule === null) {
            return false;	
        }
        if (leftVaule === rightProto) {
            return true;	
        } 
        leftVaule = leftVaule.__proto__ 
    }
}
Copy the code

In fact, the main implementation principle of Instanceof is as long as the right variable’s prototype is on the left variable’s prototype chain. Thus, instanceof iterates through the prototype chain of the left variable until it finds the prototype of the right variable. If the lookup fails, it returns false, telling us that the left variable is not an instanceof the right variable.

Let’s look at some interesting examples

function Foo() {}Object instanceof Object // true
Function instanceof Function // true
Function instanceof Object // true
Foo instanceof Foo // false
Foo instanceof Object // true
Foo instanceof Function // true
Copy the code

To fully understand how Instanceof works, in addition to the implementation principles we just mentioned, we also need to know how JavaScript prototype inheritance works.

As for the principle of prototype inheritance, I will simply use a diagram to show it

We know that every JavaScript Object has an implicit __proto__ prototype property, and the explicit prototype property is prototype. Only the object.prototype. __proto__ property is null if unmodified. Following this principle, let’s review some of the interesting instanceof uses mentioned above.

  • Object instanceof Object

    The prototype attribute of Object is object. prototype. Object is a Function created by Function. Prototype and Function. Prototype’s __proto__ attribute is Object.prototype. Object instanceof Object returns true. Let me just write it in code

    leftValue = Object.__proto__ = Function.prototype;
    rightValue = Object.prototype;
    // First judgmentleftValue ! = rightValue leftValue =Function.prototype.__proto__ = Object.prototype
    // Second judgment
    leftValue === rightValue
    / / return true
    Copy the code

    Function instanceof Function and Function instanceof Object run in the same way as Object instanceof Object.

  • Foo instanceof Foo

    Foo’s __proto__ attribute is Function. Prototype. Foo’s __proto__ attribute is Function. So Foo instanceof Foo also returns false.

    So let’s do it very briefly in code

    leftValue = Foo, rightValue = Foo
    leftValue = Foo.__proto = Function.prototype
    rightValue = Foo.prototype
    // First judgmentleftValue ! = rightValue leftValue =Function.prototype.__proto__ = Object.prototype
    // Second judgmentleftValue ! = rightValue leftValue =Object.prototype = null
    // The third judgment
    leftValue === null
    / / returns false
    Copy the code
  • Foo instanceof Object

    leftValue = Foo, rightValue = Object
    leftValue = Foo.__proto__ = Function.prototype
    rightValue = Object.prototype
    // First judgmentleftValue ! = rightValue leftValue =Function.prototype.__proto__ = Object.prototype
    // Second judgment
    leftValue === rightValue
    / / return true
    Copy the code
  • Foo instanceof Function

    leftValue = Foo, rightValue = Function
    leftValue = Foo.__proto__ = Function.prototype
    rightValue = Function.prototype
    // First judgment
    leftValue === rightValue
    / / return true
    Copy the code

conclusion

If you want to determine the specific typeof an object, you can use instanceof, but instanceof may not be accurate. If you want to determine the specific typeof an object, you can use instanceof. For example, an array can be judged as an Object by instanceof. So we want to more accurately judge the type of Object instances, can take the Object. The prototype. ToString. Call method.