typeofとinstanceofの使用シナリオと注意事項の詳細な理解

JavaScriptのデータ型は、2つのカテゴリに分類されている未定義、数値、ブール、文字、記号、BIGINT、ヌル。1つの組成物の基本型参照型のオブジェクト、機能、他のタイプからなるアレイ

ここに画像の説明を挿入
どのタイプのデータが属するかを判別する方法は、JavaScriptの非常に重要な知識ポイントです。最も一般的に使用される2つの方法は、typeofキーワードとinstanceofキーワードを使用してデータのタイプを判別することです。

typeofとinstanceofの両方を使用してデータのタイプを判断できますが、それらの間には依然として違いがあり、この違いは主に2つの側面に存在し
ます。1。異なるアクションポイント
。typeofは主に基本データのタイプを判断するために使用されます。 instanceofは、参照データのタイプを判別するために使用されます。

2.基礎となるロジックが異なります
。typeofはストレージユニット内のデータのタイプラベルに基づいてデータのタイプを決定し、instanceofは関数のプロトタイプ属性値がに存在するかどうかに基づいてデータのタイプを決定します。オブジェクトのプロトタイプチェーン。

Typeofは、「undefined」、「number」、「boolean」、「string」、「symbol」、「bigint」、「object」、「function」の合計8つの値でデータ型を判断します。

typeofを使用して、undefined、number、boolean、string、symbol、bigintのデータ型を適切に判断します。
しかし、基本型がnullであると判断すると、typeofの使用は正確ではなくなります。この問題は JavaScriptの最初のバージョン2にまでさかのぼることができます。このバージョンでは、単一の値がスタック内の32ビットストレージユニットを占有し、32ビットストレージユニットをタイプタグ(1〜3ビット)に分割できます。 )実際のデータでは、型ラベルは下位ビットに格納され、 5つのタイプに分けることができます
。1。0、1、2ビットがすべて0の場合、typeofはデータ型を「オブジェクト」と判断します
。2 。0番目のビットが1の場合、typeofはデータ型が「数値(整数)」であると判断します。3。0
番目と2番目のビットが両方とも0で、1番目のビットが1の場合、typeofはデータ型を「」と判断します。 number(浮動小数点数) ';
4. 0番目と1番目の桁が両方とも0で2番目の桁が1の場合、typeofはデータ型を「文字列」と判断します; 5.1
番目と2番目の桁が両方とも1の場合0番目のビットが0の場合、typeofはデータ型を「ブール値」と判断します。

さらに、2つの特殊なケースがあります。
未定義:整数-2 ^ 30(整数の範囲外の数値)
null:0から31までの数字はすべて0です。

データ値がnullの場合、0、1、2番目のビットがすべて0のときに、typeofが型を「object」と判断するという条件を満たすだけなので、typeof null === 'object'の結果はtrueです。

typeofを使用して関数を決定することも問題があります。IE6、7
、および8では、多くのホストオブジェクトが関数ではなくオブジェクトです。
例えば:

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

そして古いバージョンのFirefoxでは

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

このようなものがたくさんあるので、例と同じではなく、ほとんどがブラウザの実装の違いです。これで標準が統一されました。

私がie11で実行した結果:

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

Firefoxの最新バージョンで実行した結果:

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

Typeofには、次のような参照型の判断にまだいくつかの問題があります。

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

このとき、より詳細な情報が必要な場合は、instanceofキーワードを使用する必要があります。

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

instanceof演算子を使用すると、関数のプロトタイププロパティ値がオブジェクトのプロトタイプチェーンに存在するかどうかを明確に判断できます。

ただし、instanceofは完全に信頼できるわけではありません。たとえば、instanceofの判断結果は、Symbol.hasInstanceプロパティの影響を受ける可能性があります。

function Person(){
    
    
}

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

let p = new Person();

p instanceof Person;//false

ただし、Symbol.hasInstanceプロパティは、データのプロトタイプチェーンには影響しません。カスタムmyInstanceofメソッド3使用しても、Symbol.hasInstanceプロパティの影響を受けません。

/**
* 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

カスタムmyInstanceofメソッドの改良版:

/**
* 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も間違った結果を出力します

たとえば、main.htmlとiframe.htmlの2つのhtmlファイルを定義すると、コードは次のようになります。

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>

npx http-serverがmain.htmlを開いた後、結果p instanceof Object:falseを取得します。

画像

この結果の理由は次のとおりです。

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

では、なぜブラウザーはこのようにする必要があるのでしょうか?Objectコンストラクターを使用するのは良くありませんか?

このように見てみましょう:

main.htmlはウィンドウを開き(今はmain_windowと呼びます)、iframe.htmlはウィンドウを開きます(今はiframe_windowと呼びます)。
iframe_windowからp要素オブジェクトを取得します。そのプロトタイプチェーンは次のとおりです。HTMLParagraphElement.prototype-> HTMLElement.prototype-> Element.prototype-> Node.prototype-> EventTarget.prototype-> Object.prototype
次に、main.htmlファイル内のp instanceof Objectを判断します。つまり、p instanceofmain_window.Objectを判断しますp要素はiframe.htmlファイルで作成されるため、p instanceof iframe_window.Object === trueです。あなたがしたい場合のp instanceofはmain_window.Object ===は本当
次に、iframe_window.Object === main_window.Objectを満たすためにしかし、この条件は絶対に満たされていません。if i frame_window.Object === main_window.Object、次に、iframe.htmlファイルのObject関数を変更すると、main.htmlに適用され、重大なセキュリティ問題と一連の不可解なバグが発生します。

したがって、異なるグローバル変数の下にある同じ名前のコンストラクターは同じ関数ではありません。これにより、データと関数が異なるグローバル変数の下にある場合、instanceofはエラーを判断します

ただし、この例でtypeof使用すると問題を解決できます。データがヌル型であるかどうかを確認することを忘れないでください。

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

typeofはストレージユニット内のタグタイプを判断するため、影響を受けません。

参照


  1. 私は通常、基本的なタイプの小文字から始めます。↩︎

  2. 現時点では、SymbolとBigIntはないため、これらは議論の対象外です。↩︎

  3. myInstanceofメソッドの起源↩︎

おすすめ

転載: blog.csdn.net/qq_35508835/article/details/113764707