タイプとは何ですか?簡単に言えば、タイプとは、メモリ内のバイナリ シーケンスに特定の意味を割り当てることです。たとえば、バイナリ シーケンス 0100 0000 0111 0000 0001 0101 0100 1011 1100 0110 1010 0111 1110 1111 1001 1110 は、64 ビット符号なし整数として表示すると 4643234631018606494 になります。 ger 型。浮動小数点数の 2 進表現に関する 54 の規則 (付録 1 を参照)精度 浮動小数点型は 257.331 です。
ほとんどのコンピューター言語では、データの保存と表現に変数を使用します。一部の言語では、変数の型を (コンパイル時でも実行時でも) 変更できません。対照的に、JavaScript やその他の言語の変数は任意の型を格納でき、型なしの変数を使用します。変数の型が存在するかどうかは、構文とは関係ありません。たとえば、C# では var 型の変数も提供されます。ただし、次のステートメントは C# でエラーを引き起こします。
変数a=1;
a="文字列";
その理由は、C# の var キーワードは変数の型宣言を省略するだけで、初期化式に基づいて変数の型を自動的に推測するため、C# の var 変数には型が存在するためです。 JavaScript では、いつでも特定の変数に任意の値を割り当てることができるため、JavaScript 変数は型指定されません。
コンピュータ言語の型システムの設計方法によれば、強い型と弱い型の 2 つのタイプに分類できます。 2 つの違いは、異なる型間の暗黙的な変換が計算中にユーザーに透過的に行われるかどうかにあります。ユーザーの観点から見ると、言語がすべての型を暗黙的に変換できる場合、その変数、式などが操作に関与する場合、型が正しくない場合でも、暗黙的な変換を通じてユーザーに対して正しい型を取得できます。 、あたかもすべての型がすべての操作を実行できるかのように、そのような言語は弱い型付けと呼ばれます。対照的に、厳密に型指定された言語では、型間の暗黙的な変換が必ずしも存在するとは限りません (たとえば、C++ は厳密に型指定された言語ですが、C++ では double と int を相互に変換できますが、double と any の間にはキャストが必要です)。ポインタの種類)
型はプログラマーが正しいプログラムを作成するのに役立ち、実際のプログラム作成プロセスでは制約として機能します。一般的なルールとして、制約が強いほどエラーは発生しにくくなりますが、プログラムを書くのは面倒になります。型を持つ変数を持つ強く型指定された言語は最も強い制約を持ち、その代表的なものは C++ であり、型を持たない変数を持つ弱い型指定言語は最も弱い制約を持ち、その代表的なものは JavaScript です。 JavaScript では制約が比較的弱いため、このエラーが発生しやすくなります。
変数 a =200;
var b="1";
var c= a + b;
c は 201 であると予想されるかもしれませんが、実際には「2001」であり、厳密に型指定された言語では決して発生しないエラーです。ただし、JavaScript にはこれらの制約がないため、数値型と文字列型を簡単に連結できます。したがって、制約と柔軟性は常に、言語設計者にとってバランスを取る必要がある一連の機能です。
型は、型チェックを通じて機能する制約です。言語が異なれば、型チェックはさまざまな段階で機能します。これらの段階は、コンパイル時チェックと実行時チェックに分けることができます。 JavaScript のようなインタープリター型言語の場合、字句解析と構文解析というコンパイル プロセスに似た段階があり、インタープリター型言語の型チェックが構文解析またはその前段階で完了している場合は、それも考慮できます。コンパイル時のチェックと同様です。したがって、より合理的なステートメントは、静的型チェックと動的型チェックです。
興味深いことに、多くの言語はコンパイル時に型をチェックしますが、その型情報は実行時にも取得できます。たとえば、C# では、実行時に型情報を取得して使用できます。
JavaScript は設計のあらゆる面で柔軟性を優先するため、動的型チェックを使用し、ごく少数の特定の操作を実行する場合を除いて型を積極的にチェックしません。実行時に任意の変数または式の型情報を取得し、プログラム ロジックを通じてその正確性をチェックできます。
JavaScript 標準では次の 9 種類が指定されています。 未定義の Null ブール文字列 数値 オブジェクト参照リストの補完
このうち、3 種類の Reference List Completion は言語解析実行時にのみ使用され、プログラムから直接アクセスすることはできません。以下では、これら 6 つのタイプについて説明します。
未定義型には値が 1 つだけあり、これは変数に値が割り当てられていない場合の値です。実際、JavaScript では未定義を表すことができます。値を変更するには、グローバル未定義プロパティに値を割り当てます。
Null 型にも null という 1 つの値しかありませんが、JavaScript ではこの一意の値を表すキーワード null が提供されます。 Null 型のセマンティクスは「空のオブジェクト参照」です。
ブール値には true と false の 2 つの値があります。
String 型の正式な解釈は、16 ビットの符号なし整数型のシーケンスであり、実際には UTF-16 でエンコードされたテキスト情報を表すために使用されます。
JavaScript の Number には、合計 18437736874454810627 (つまり、264 ~ 253 +3) の値があります。 JavaScript の Number は倍精度浮動小数点型で格納されますが、9007199254740990 は NaN を表し、IEEE 754 (付録 1 を参照) に準拠し、64 ビットと 8 バイトを占有します。
JavaScript で最も複雑なタイプは Object で、これは一連のプロパティの順序付けされていないコレクションです。 Function はプライベート プロパティ [[call]] を実装するオブジェクトです。JavaScript ホストはいくつかの特別なオブジェクトも提供します。
JS 標準で指定されている型については前に説明しましたが、無視できない問題は、JS 標準が JS 実装者向けに作成されているということです。たとえば、型は必ずしも標準に従って定義される必要はありません。 , JS は . 操作を実行すると、非オブジェクト型は自動的に対応するオブジェクトに変換されるため、この観点からは、"str".length は実際には (new String("str").length と同等であると考えられます)。両方が同じタイプであることは悪いことではありません。実行時の型判別には JS のいくつかの言語機能を使用しますが、これらの方法の結果は異なります。どちらが良いか悪いかを判断する必要があります。
typeof は JS 言語の演算子であり、JavaScript 標準に従って、変数の型名の文字列表現を取得するために使用されます。ブール、数値、未定義、オブジェクト、関数、および JavaScript 標準により、実装者は一部のオブジェクトの値の typeof をカスタマイズできます。
JS 標準には次のような記述リストがあります。
タイプ | 結果 |
未定義 | "未定義" |
ヌル | "物体" |
ブール値 | 「ブール値」 |
番号 | "番号" |
弦 | "弦" |
オブジェクト (ネイティブで [[call]] を実装しません) | "物体" |
オブジェクト (ネイティブで [[call]] を実装) | "関数" |
オブジェクト(ホスト) | 実装に依存 |
次の例は 51js の Rimifon からのもので、IE の typeof の結果が「date」と「unknown」を生成する状況を示しています。
var xml=document.createElement("xml");
var rs=xml.レコードセット;
rs.Fields.Append("日付", 7, 1);
rs.Fields.Append("bin", 205, 1);
rs.Open();
rs.AddNew();
rs.Fields.Item("日付").Value = 0;
rs.Fields.Item("bin").Value = 21704;
rs.Update();
var date = rs.Fields.Item("date").Value;
var bin = rs.Fields.Item("bin").Value;
rs.Close();
アラート(日付);
アラート(ビン);
alert([日付の種類, ビンの種類]);
try{alert(date.getDate())}catch(err){alert(err.message)}
「型」セマンティクスに最も近いこの判定方法については、実際には多くの批判があります。その 1 つは、typeof を使用すると、新しい文字列 ("abc") と新しい数値 (123) を区別できないということです。 JSプログラミングでは、さまざまなオブジェクトが多数使用されることが多く、typeofではすべてのオブジェクトに対して「オブジェクト」という曖昧な結果しか得られず、実用性が大きく低下します。
instanceof の意味は中国語に翻訳すると、「... のインスタンスです」と解釈されます。文字通り理解すると、これはクラスベースのオブジェクト指向プログラミングに基づいた用語であり、JS は実際にはクラスベースのプログラミングをサポートしません。言語レベル。 JavaScript 標準では何も言及されていませんが、実際には、一部の組み込みオブジェクトの設計と演算子の設定はすべて、クラスを実装する「公式」方法、つまり、新しい演算子が動作するときに関数をクラスとして使用する方法を示唆しています。関数では、関数のプロトタイプ属性が新しく構築されたオブジェクトのプロトタイプに設定され、関数自体がコンストラクターとして使用されます。
したがって、同じ関数の新しい操作から構築されたオブジェクトは、クラスのインスタンスとみなされます。これらのオブジェクトの共通点は、1. 同じプロトタイプを持ち、2. 同じコンストラクターによって処理されることです。そしてinstanceofは、このようなクラスの実装方法と連動して「インスタンスがクラスに属しているかどうか」をチェックする演算子です。また、オブジェクトがコンストラクターによって処理されたかどうかを確認することは非常に困難ですが、そのプロトタイプが何であるかを確認することははるかに簡単であると推測できます。したがって、instanceof の実装はプロトタイプの観点から理解されます。 [ [prototype]] 属性が特定の関数のプロトタイプと一致していることを確認します。ここでの [[prototype]] はプライベート プロパティであり、SpiderMonkey (Firefox の JS エンジン) の __proto__ を使用してアクセスできることに注意してください。
プロトタイプは、標準で記述されているオブジェクト型に対してのみ意味があるため、オブジェクト以外のすべてのオブジェクトに対しては、instanceof は false を取得し、instanceof は、特定の型に属するかどうかを判断することしかできず、型を取得することはできません。また、instanceof は、定義された「クラス」から構築されたオブジェクトであることも明らかです。
実際、instanceof は騙すことができますが、それが使用するオブジェクトのプライベート属性 [[prototype]] は変更できませんが、関数のプロトタイプはパブリック属性です。次のコードは、instanceof を騙す方法を示しています。
関数 ClassA(){};
関数 ClassB(){};
var o = new ClassA();//クラス A のオブジェクトを構築します
ClassB.prototype = ClassA.prototype //ClassB.prototype を置き換えます。
alert(oinstanceofClassB)//真の欺瞞が成功しました - -!
Object.prototype.toString は元々呼び出しが難しく、JavaScript の組み込みクラスはすべて toString メソッドをカバーします。非組み込みクラスで構築されたオブジェクトの場合、Object.prototype.toString はこの種の意味のない [object Object ] しか取得できません。結果。したがって、かなり長い間、この機能の魔法の効果は発見されていませんでした。
標準ではObject.prototype.toStringの記述はわずか3文です
1. このオブジェクトの [[class]] 属性を取得します
2. 3 つの文字列「[object」、 result(1)、および「]」を連結して文字列を計算します。
3. 結果 (2) を返します。
明らかに、Object.prototype.toString は実際にはオブジェクトの [[class]] 属性を取得するだけですが、それが意図的であるかどうかはわかりません。すべての JS 組み込み関数オブジェクト String Number Array RegExp... がすべて使用されます。 new を使用してオブジェクトを構築する場合は、 [[class]] 属性を型を判断するための適切な基準として使用できるように、 [[class]] 属性を設定します。
Object.prototype.toString はこのオブジェクトのプロパティを取得するため、このオブジェクトを指定して、Object.prototype.toString.call または Object.prototype.toString.apply を使用して型を取得できます。
Object.prototype.toString は賢いのですが、カスタム関数が [[class]] を設定せず、プログラム内でこのプライベート プロパティにアクセスできないため、カスタム関数によって構築されたオブジェクトの型を取得できません。 Object.prototype.toString の最大の利点は、1 と new Number(1) を同じタイプのオブジェクトにできることです。ほとんどの場合、この 2 つは同じ方法で使用されます。
ただし、このときに new Boolean(false) が bool 演算に参加すると、結果は false の逆になります。このとき 2 つが同じ型であるとみなされると、簡単にエラーが発生する可能性があることに注意してください。チェック。
上記3種類の判定方法を比較するために、複数の判定方法を誰でも総合的に比較できるように表を作成しました。比較しやすいように、複数の判定方法による結果を統一しました。
物体 | の種類 | インスタンスの | オブジェクト.プロトタイプ.toString | 標準 |
「ABC」 | 弦 | —— | 弦 | 弦 |
新しい文字列("abc") | 物体 | 弦 | 弦 | 物体 |
関数 hello(){} | 関数 | 関数 | 関数 | 物体 |
123 | 番号 | —— | 番号 | 番号 |
新しい番号(123) | 物体 | 番号 | 番号 | 物体 |
新しい配列(1,2,3) | 物体 | 配列 | 配列 | 物体 |
newMyType() | 物体 | 私のタイプ | 物体 | 物体 |
ヌル | 物体 | —— | 物体 | ヌル |
未定義 | 未定義 | —— | 物体 | 未定義 |
実際、上記の方法のどれがより合理的であるかを言うのは困難です。標準の規定ですら、ベストな使用方法ではなく、JS の実行時のメカニズムを反映しているだけです。私の個人的な意見は、「型」の概念を軽視し、「このオブジェクトをどのように使用するか」という制約にもっと重点を置くことです。typeof とinstanceof を使用してチェックすると、必要に応じて厳密に型指定された言語と同じ効果を達成できます。
符号ビット: 正負の符号を表すために使用されます。
指数: 累乗の数値を表すために使用されます。
仮数 (mantissa): 精度を示すために使用されます。