JSデータ型検出のさまざまな方法の概要

JSデータ型検出のさまざまな方法の概要

背景

ご存知のとおり、js は動的型システムとプロトタイプベースの継承を使用する、動的な弱い​​型付けのスクリプト言語です。

型に対する静的制約がないため、データ型に起因するプログラム エラーはコンパイル段階では検出できません。堅牢なコードを書くには、実行時にさまざまなチェックと互換性を実行する必要があります。そのため、データ型を巧みに正確に検出する能力は、この言語を習得するための最も重要な基礎の 1 つとなっています。

データ型を決定する方法は何ですか?

一般的には、typeof、instanceof、Object.prototype.toString、コンストラクタ、ダックタイピング、特定の型の検出メソッドArray.isArray()、Number.isNaN()などが大まかに挙げられます。メソッドは多数ありますが、使用シナリオは異なります。

1. typeof を使用して基本データ型を決定します。

戻り値は、未定義、文字列、数値、ブール値、オブジェクト、関数、シンボルです。

公式に提供されている型検出演算子である typeof は、undefined、文字列、ブール値、シンボルなどの基本的なデータ型と関数を検出する際に非常に信頼性が高いことがわかります。パフォーマンスが低迷した主な理由は

1) 特定のオブジェクト タイプ (配列、日付、正規表現) を区別できません。
2) typeof null === 'object' // は真です。 。 。 。

欠陥 2) は、オブジェクト参照の型を決定するときに、typeof x === 'object' && x !== null という 1 つの文を追加することで回避できます。しかし、特定の種類のオブジェクトを区別できないことは、確かに大きな問題点です。

2. instanceofを使用してオブジェクトのデータ型を決定する

この演算子は、コンストラクターのプロトタイプがターゲット オブジェクトのプロトタイプ チェーンに出現するかどうかを検出するために使用されます。

これは予測検出方法です。typeof のように文字列としてデータ型を直接返すのではなく、オブジェクト型のコンストラクタを予測し、最終的にブール値を返す必要があります。

検出ルールは実際には名前から確認でき、インスタンスが特定のコンストラクターによって作成されたかどうかを判断します。原理がわかったので、独自の instanceof を実装してみましょう。

関数 myInstanceof(ターゲット,コンストラクタ){
  const baseType = ['文字列', '数値', 'ブール値', '未定義', 'シンボル']
    baseType.includes(typeof(target)) の場合 { false を返す }
    //プロトタイプチェーンは実際にはオブジェクトで構成されたリンクリストです。このリンクリストを走査すると、
  プロトタイプを Object.getPrototypeOf(target); とします。
    while(プロトタイプ){
        //チェーン上に一致するオブジェクトがある場合はtrueを返します
      if (プロトタイプ === コンストラクター.プロトタイプ) {
        真を返す
      }それ以外{
        プロトタイプ = Object.getPrototypeOf(プロトタイプ)
      }
    }
    偽を返す
}
コンソールログ(myInstanceof([],配列))

js では、インスタンスはコンストラクターを通じて作成されますが、コンストラクター自体は感情のない生産マシンにすぎないため、すべてがオブジェクトから発生すると広く考えることができます。インスタンスの魂と特性 (パブリック プロパティとメソッド) はすべて、コンストラクターの prototype プロパティによって指されるプロトタイプ オブジェクトから共有され、プロトタイプ オブジェクトはすべて純粋なオブジェクトであり、Object コンストラクターによって作成されます。これにより、次の結果が発生します。

配列の場合、プロトタイプ チェーン上のオブジェクトを走査すると、Array.prototype Object.prototype が表示されます。

また、リテラル値で作成された基本データ型を判定することはできません。例えば

上記の欠陥を補うにはどうすればよいでしょうか。その答えは、上記の特殊なシナリオでは、instanceof の代わりに次のコンストラクターを使用することです。

3. コンストラクター属性を使用する

まず、はっきりさせておきましょう。コンストラクターはプロトタイプのプロパティであり、インスタンスはプロトタイプから継承されるため、このプロパティはインスタンス上で直接アクセスすることもできます。
まず、コンストラクタの普遍的なパフォーマンスを見てみましょう

予想以上に良いパフォーマンスで、null と undefined を除けば、基本 (ラッパー) 型やコンストラクタ属性を持つオブジェクト型を正確に判定できます。

instanceof のようにプロトタイプ チェーン全体を走査するのではなく、インスタンスのみで判断するため、Array|Object を正確に区別できます。しかし、致命的な欠陥もあります。インスタンス上のこの属性は変更が簡単すぎるのです。変更されると、このメソッドは意味をなさなくなります。

4. toString メソッド

まず、js のオブジェクト型、つまりパッケージング オブジェクトの基本型には toString メソッドがあります。 Object.prototype.toString() から継承され、呼び出しは対応するタイプの文字列タグ "[object Type]" を返します。

この方法は、主人を殴って殺すようなもので、意図せず柳を植えて柳の森を作ったような感じです。本来の機能は、オブジェクトを表す文字列を取得することだけです。現在、js の型検出に使用されており、パフォーマンスは非常に優れています。基本型とオブジェクト型の両方で非常に優れたパフォーマンスを発揮します。欠点を指摘するとすれば、返される文字列が少し複雑で、あまり使い勝手がよくないことだけです。では、単純化してみましょう。

まずは簡略版を書く

関数isType(型,値){
    Object.prototype.toString.call(value) === `[object ${type}]` を返します
}
console.log(isType('配列',[]))
console.log(isType('数値',1))

これはあまり使い勝手がよくありません。 'Array' や 'Number' などの型パラメータはスペルミスしやすいです。そのため、メソッドでパラメータを事前に設定し、isArray などの関数を呼び出して返す関数ファクトリを構築できることが期待されます。 IDE では、関数名には文字列よりも優れたコードヒントがあり、スペルミスが発生する可能性が低くなります。

関数isType(type){
    関数(値)を返す{
        Object.prototype.toString.call(value) === `[object ${type}]` を返します
    }
}

const isArray = isType('配列')
定数isNumber = isType('Number')
コンソールログ(isArray([]),isNumber(1))

ここでは高階関数の考え方が使われており、パラメータを保持しながら新しい関数を返します。js では、bind はこれをバインドするだけでなく、パラメータを保持しながら新しい関数を返すこともできると考えられますが、これもここで適しています。

関数isType(型,値){
    Object.prototype.toString.call(value) === `[object ${type}]` を返します
}

const isArray = isType.bind(null,'Array')
定数 isNumber = isType.bind(null,'Number')
コンソールログ(isArray([]),isNumber(1))

さらに一歩進んで、パラメータカリー化の考え方を使って変換することができます。

関数isType(型,値){
    Object.prototype.toString.call(value) === `[object ${type}]` を返します
}
関数curring(fn,...args1){
    len = fn.length;とします。
    関数を返す(...args2){
        定数args = args1.concat(args2);
        (引数の長さ<len)の場合{
            戻り値:curring(fn,...args)
        }それ以外{
            fn(...args) を返します
        }
    }
}
const isArray = curring(isType,'Array')
const isNumber = curring(isType,'Number')
コンソールログ(isArray([]),isNumber(1))

最後に、サポートされるタイプを充実させれば完了です。

定数型 = [
    「ヌル」、
    '未定義'、
    '弦'、
    '番号'、
    「ブール」、
    '物体'、
    '配列'、
    '日付'、
    '関数'、
    '正規表現',
    'シンボル'、
    '数学'、
]
定数チェックタイプユーティリティ = {}
types.forEach((type)=>{
    checkTypeUtil[`is${type}`] = curring(isType,type)
})
輸出 {
 チェックタイプユーティリティ
}
コンソールログ(checkTypeUtil.isArray([]))

5. Array.isArrayを使用して配列を決定する

前述のように、instanceof は配列を検出するために使用できます。ただし、iframe によって作成されたマルチウィンドウ環境では、ウィンドウのグローバル環境を分離する必要があるため、各ウィンドウで Array と Array.prototype が異なる必要があります。したがって、iframeA.Array.prototype ≠ iframeB.Array.prototype であるため、iframeA.arr instanceof iframeB.Array は false を返す必要があります。これは確率の低いイベントですが、iframe を使用するシナリオでは、値を相互に渡すことも非常に起こり得ます。 ES6 で提供されている Array.isArray を使用するとこの問題は発生せず、配列を正確に判断できます。

このように投票することができます

Array.isArrayの場合
  Array.isArray = 関数(x) {
    Object.prototype.toString.call(x) === '[オブジェクト配列]'を返します。
  };
}

6. ArrayLikeとArrayを区別する

配列クラスの定義は次のとおりです。

  • 長さプロパティを持ち、他のプロパティ(インデックス)は負でない整数です(オブジェクト内のインデックスは文字列として扱われます)
  • 配列が持つメソッドを持たない
関数isLikeArray(x){
    if(!(typeof x === 'object' && x !== null)){
        偽を返す
    }
    戻り値の型は x.length === 'number' && x.length >= 0 && !Array.isArray(x) です
}

配列のようなオブジェクトは、Array.from Array.prototype.slice.call(val) を使用して実際の配列に変換できます。

7. オブジェクトが純粋オブジェクト(または通常のオブジェクト)であるかどうかを判断する

純粋オブジェクトの定義: 具体的には、次の3つの方法で作成されたオブジェクトを指します。

  • 新しいオブジェクト
  • オブジェクトリテラルの作成 {}
  • オブジェクトを作成します(null)

jqueryとlodashのソースコードは、次の方法で検出されます。

const funcToString = Function.prototype.toString
const objectCtorString = funcToString.call(Object)

関数isPlainObject(値){
    // toString を使用して最初に他のデータ型を除外します if(!value || !Object.prototype.toString.call(value) === "[object Object]"){
        偽を返す
    }
    const proto = Object.getPrototypeOf(値)
    if(proto === null){//Object.create(null) と互換性があり、true を返します。
    }
    const Ctor = Object.prototype.hasOwnProperty.call(proto,'constructor') && proto.constructor;
    if(typeof Ctor !== 'function'){
        偽を返す
    }
    // ここでは、上記の複数のウィンドウ環境で異なるオブジェクトが発生する問題を回避するために、instanceof を直接使用するのではなく、文字列を使用してコンストラクターがオブジェクトであるかどうかを判断します。if(funcToString.call(Ctor) === objectCtorString){
        真を返す
    }
    偽を返す
}
コンソールログ(isPlainObject(Object.create(null)))
console.log(isPlainObject(新しいオブジェクト))
コンソールログ(isPlainObject({a:1}))

8. NaN を検出するにはどうすればいいですか? Number.isNaN と isNaN の違いは何ですか?

結論: Number.isNaN は、渡された値が NaN と直接等しいかどうかを厳密に判断します。

isNaN はまず Number() 変換を実行し、次にそれが NaN かどうかを判断します。

9. ダックタイピング

実際、上記のコンストラクタを使用してデータ型を決定するのは、このメソッドの使用です。ある動物がアヒルであるかどうかを判断するには、アヒルのように見えるかどうか、アヒルのように鳴くかどうかといった単純な経験的判断に基づいて大まかな判断を下すことができます。

例えば、オブジェクトがPromiseかどうかを判断するには、次のようにします。

関数isPromise(x){
    if (!(x インスタンスの Promise)) {
        偽を返す
    }
    戻り値の型は x.then === 'function' です
}

要約する

JS データ型検出に関するこの記事はこれで終わりです。より関連性の高い JS データ型検出コンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き閲覧してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • JavaScript の基礎におけるデータ型の詳細な説明
  • JSにおける4つのデータ型判定方法
  • データ型の判断における js typeof と instanceof の違いと、その開発と使用について
  • JavaScriptの基本構文とデータ型の詳細な説明
  • この記事では、jsのデータ型とデータ構造の世界を紹介します。
  • JS を使用してデータ型を決定する 4 つの方法
  • JavaScript の 7 つのデータ型の詳細な説明

<<:  MySQL トランザクションの概念と使用法の詳細な説明

>>:  Tencent Cloudでhive3.1.2を構築する方法を教えます

推薦する

IDEAでVUEプロジェクトをデバッグするための詳細な手順

js コードをデバッグするには、コード内にデバッガーを記述するか、Chrome で毎回ブレークポイン...

Ajax は CORS レスポンス ヘッダーを設定してクロスドメインの問題を解決し、クロスドメインのケース スタディを実現します。

1. クロスドメインを実現するためにCORSレスポンスヘッダーを設定するクロスオリジンリソース共有...

Mysqlトランザクション操作の失敗を解決する方法

Mysqlトランザクション操作の失敗を解決する方法トランザクションの原子性: トランザクションは、デ...

MySQL ストアド プロシージャの in、out、inout パラメータの例と概要

ストアドプロシージャ1. ストアドプロシージャを作成し、グローバル変数を表示する mysql>...

TypeScript を使用して Vue3 で axios をカプセル化する詳細な例

この axios パッケージは、vue3 デモで使用されます。便宜上、element-plus は ...

Centos で MySQL パスワードを変更する方法

1. MySQL ログイン設定を変更します。 # vim /etc/my.cnf文を追加: skip...

MySql8.0 のトランザクション分離レベルエラーの問題を解決する

目次MySql8.0 トランザクション分離レベルエラーの表示質問コマンドは次のように変更されますMy...

MySQL5.7+ MySQL Workbenchのインストールと設定方法のグラフィックチュートリアル(MAC)

この記事では、主にMACオペレーティングシステムでのMySQL5.7とMySQLWorkbenchの...

MacBook 向け Python 3.7 インストール チュートリアル

MacBookにpython3.7.0をインストールする詳細な手順は、参考までに記録されています。具...

Tomcat と WebLogic で純粋な HTML ファイルを展開するプロセスの分析

1. まず、純粋なHTMLファイルにはindex.htmlというエントリが必要です。 2. Tomc...

Linux ファイルディレクトリ管理コマンドの概要

タッチコマンドこれには 2 つの機能があります。1 つは、既存のファイルの時間タグを現在のシステム時...

画像にマウスを置いたときにズームイン/ズームアウトするには JS を使用します

マウスが画像上にあるときにズームインおよびズームアウトするには、JS を使用します。具体的なコードは...

MySQLの始め方から諦め方まで徹底解説 - インストール

学ぶ内容1. ソフトウェアのインストールとサーバーの設定。 2. (オプションですが、強くお勧めしま...

MySQL スロークエリログの基本的な使い方チュートリアル

スロークエリログ関連のパラメータMySQL スロー クエリ関連のパラメータの説明: slow_que...

Vue.js と MJML でレスポンシブなメールを作成する

MJML は、開発者が美しく、応答性に優れ、あらゆるデバイスやメール クライアントで動作する魅力的な...