In-depth understanding of the usage scenarios and precautions of typeof and instanceof

JavaScript data type is divided into two categories, undefined, Number, Boolean, String, Symbol, BIGINT, null . 1 basic type of composition and reference type Object, Function, Array composed of other types .

Insert picture description here
How to determine which type of data belongs to is a very important knowledge point in JavaScript. The two most commonly used methods are to use typeof and instanceof keywords to determine the type of data.

Although both typeof and instanceof can be used to judge the type of data, there are still differences between them, and this difference mainly exists in two aspects:
1. The different points of action;
typeof is mainly used to judge the basic data Type, instanceof is used to determine the type of reference data.

2. The underlying logic is different;
typeof is to determine the type of data based on the type label of the data in the storage unit, and instanceof is to determine the type of data based on whether the prototype attribute value of the function exists in the prototype chain of the object.

Typeof judges the data type with a total of 8 values, which are'undefined','number','boolean','string','symbol','bigint','object' and'function'.

Use typeof to judge the data types undefined, number, boolean, string, symbol and bigint well.
But when judging the basic type is null, using typeof is no longer accurate. This problem can be traced back to the first version 2 of JavaScript . In this version, a single value occupies a 32-bit storage unit in the stack, and the 32-bit storage unit can be divided into type tags (1-3 bits ) And the actual data, the type label is stored in the lower bits , which can be divided into 5 types:
1. When the 0th, 1st and 2nd bits are all 0, typeof judges the data type to be'object';
2. When When the 0th bit is 1, typeof judges the data type to be'number (integer)';
3. When the 0th and 2nd bits are both 0, and the 1st bit is 1, typeof judges the data type to be'number (Floating point number)';
4. When the 0th and 1st digits are both 0 and the 2nd digit is 1, typeof judges the data type to be'string';
5. When the 1st and 2nd digits are both 1, and the 0th bit is 0, typeof judges the data type to be'boolean';

In addition, there are two special cases:
undefined: integer −2^30 (number outside the range of integer)
null: digits 0 to 31 are all 0

When the data value is null, it just satisfies the condition that typeof judges the type as'object' when the 0th, 1st and 2nd bits are all 0, so the result of typeof null ==='object' is true.

Using typeof to determine function is also problematic:
on IE 6, 7 and 8, many host objects are objects rather than functions.
E.g:

typeof alert === 'object';//true

And in the old version of Firefox

typeof /[0-9]/ === 'function';//true

There are many of them like this, so they are not the same as examples. Most of them are browser implementation differences. Now the standard has been unified.

The results I run on ie11:

typeof alert === 'function';//true

The results of running on the latest version of Firefox:

typeof reg === 'object';//true

Typeof still has some problems in judging reference types, such as:

typeof {
    
    } === 'object';//true
typeof [] === 'object';//true
typeof window === 'object';//true
typeof new Map() === 'object';//true

At this time, if you want to know more detailed information, you need to use the instanceof keyword.

alert instanceof Function;//true
({
    
    }) instanceof Object;//true
([]) instanceof Array;//true
window instanceof Window;//true
(new Map()) instanceof Map;//true

Using the instanceof operator, we can clearly determine whether the prototype property value of the function exists on the prototype chain of the object.

However, instanceof is not completely credible. For example, the judgment result of instanceof can be affected by the Symbol.hasInstance property:

function Person(){
    
    
}

Object.defineProperty(Person,Symbol.hasInstance,{
    
    
    value : function(){
    
    
        return false;
    }
})

let p = new Person();

p instanceof Person;//false

However, the Symbol.hasInstance property does not affect the prototype chain of the data. Using the custom myInstanceof method 3 will not be affected by the Symbol.hasInstance property:

/**
* obj 变量
* fn 构造函数
*/
function myInstanceof(obj,fn){
    
    
    let _prototype = Object.getPrototypeOf(obj);
    if(null === _prototype){
    
    
        return false;
    }
    let _constructor = _prototype.constructor;
    if(_constructor === fn){
    
    
        return true;
    }
    return myInstanceof(_prototype,fn);
}

function Person(){
    
    
}

Object.defineProperty(Person,Symbol.hasInstance,{
    
    
    value : function(){
    
    
        return false;
    }
})

let p = new Person();

p instanceof Person;//false

myInstanceof(p,Person);//true

An improved version of the custom myInstanceof method:

/**
* obj 变量
* fn 构造函数
*/
function myInstanceof(obj,fn){
    
    
    let _prototype = Object.getPrototypeOf(obj);
    if(null === _prototype){
    
    
        return false;
    }
    let _constructor = _prototype.constructor;
    if(_constructor[Symbol.hasInstance]){
    
    
        return _constructor[Symbol.hasInstance](obj);
    }
    if(_constructor === fn){
    
    
        return true;
    }
    return myInstanceof(_prototype,fn);
}

function Person(){
    
    
}

Object.defineProperty(Person,Symbol.hasInstance,{
    
    
    value : function(){
    
    
        return false;
    }
})

let p = new Person();

p instanceof Person;//false

myInstanceof(p,Person);//false

Instanceof will also output wrong results when the data and type are not under a global variable

For example, now define two html files, main.html and iframe.html, the code is as follows:

main.html

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
        <title>main</title>      
        <script type="text/javascript">
            window.onload = function(){
     
     
                console.log('document.domain : ' + document.domain);
                let iframe = document.documentElement.getElementsByTagName('iframe')[0];
                let p = iframe.contentWindow.window.document.documentElement.getElementsByTagName('p')[0];
                console.log('p instanceof Object : ' + (p instanceof Object));//p instanceof Object : false
            }
        </script>  
    </head>
    <body>
        <iframe src="./.html"></iframe>
    </body>
</html>

iframe.html

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
        <title>iframe</title>       
    </head>
    <body>
        <p>1</p>
    </body>
</html>

After npx http-server opens main.html, I get the result p instanceof Object: false

image

The reasons for this result are:

iframe.contentWindow.window.Object === window.Object;//false

So why should the browser be like this? Is it not good to use an Object constructor?

Let’s look at it this way:

main.html opens a window (we call it main_window now), iframe.html opens a window (we call it iframe_window now).
We now get a p element object from iframe_window, its prototype chain is- HTMLParagraphElement.prototype -> HTMLElement.prototype -> Element.prototype -> Node.prototype -> EventTarget.prototype -> Object.prototype .
Then we judge p instanceof Object in the main.html file , that is, judge p instanceof main_window.Object . The p element is constructed in the iframe.html file, so p instanceof iframe_window.Object === true . If you want p instanceof main_window.Object === true .
Then to satisfy iframe_window.Object === main_window.Object . But this condition is definitely not satisfied. If i frame_window.Object === main_window.Object, Then if I modify the Object function in the iframe.html file, it will be applied to main.html, which will cause serious security issues and a series of inexplicable bugs.

Therefore, the constructors with the same name under different global variables are not the same function, which causes instanceof to judge errors when the data and function are under different global variables .

However, using typeof in this example can solve the problem, just remember to determine whether the data is of null type:

null !== p && typeof p === 'object';//true

Because typeof judges the tag type in the storage unit, it will not be affected.

reference


  1. I usually start with a lowercase letter for the basic types. ↩︎

  2. At this time, there is no Symbol and BigInt, so they are not in the scope of discussion. ↩︎

  3. The origin of the myInstanceof method . ↩︎

Guess you like

Origin blog.csdn.net/qq_35508835/article/details/113764707