「単純なのは難しい。」 JSデータ型。 プリミティブとオブジェクトの真実を求めて

「単純なものは難しい」と題した一連の記事を書くことにしました。 このシリーズはJavaScript言語専用です。 なぜ「単純なものが難しい」のですか? なぜなら、データ型から始めて、インタプリタの機能を説明するからです。 これはすべて、後でJavaScriptやその他のパタ​​ーンでの継承の方法など、複雑なことだけを伝えることができるように行われます。



JavaScriptは、プロトタイプ編成を備えたオブジェクト指向プログラミング言語です。

「プロトタイプ組織」とは、次の記事で説明します(間違いなくそうなります)が、なぜ「オブジェクト指向」であり、JSのすべてがオブジェクトであるかどうかについては、今日学びます。

目標を達成するために、JSには9つのタイプが必要です。 さらに、プログラムで使用できるのは6つだけで、残りの3つは実装レベルでのみ使用可能であり、仕様で使用されます。 一見すると(これが最初の誤解です)JSのすべてはオブジェクトです。 そのため、プログラムで使用できる6つのタイプのうち5つはいわゆるプリミティブであり、オブジェクトではありません(以下では、オブジェクトと混同される理由と方法について説明します)。 これらの5つのプリミティブは次のとおりです。



-文字列(s = 'str')

-数(n = 10)

-ブール値(b = true)

そして、私はそれらを「哲学的タイプ」と呼んでいます。

-null(v = null)

-未定義(u =未定義)



そのヌルの哲学は、変数に何も割り当てられていないことを意味し、未定義は、変数にvoidが割り当てられていることを意味します。 この場合の「無」と「空」の違いは何ですか?余暇に考えてください。 今はこれを行いません。



プログラムで使用できる6番目のタイプ(オブジェクト)は次のとおりです。

- オブジェクト (Objectコンストラクタと混同しないでください。抽象型についてのみ説明しています!)-JavaScriptでオブジェクトを表す唯一の型です。

オブジェクトはデータ構造(それらのセット全体)であり、キーと値のペアのセットとして提示されます。 値は任意のデータ型にすることができます-オブジェクトのプロパティまたは関数になります-そして、オブジェクトのメソッドになります。



プリミティブを操作するには、非常に多くの方法があります。 リテラルまたはコンストラクターを介して変数に変数を割り当てることができるという事実から始まり、プリミティブを変数に宣言できないという事実で終わり、それらを直接操作します。 プリミティブは、グローバル変数とローカル変数に含めることもできます。



以下に例を示します。



var v1; //undefined ()   var v2='2'; //    var v3 = new String(2); // ,    .     String v4 = String(2); //     .   window.v4 '2'.length; //            34..toString(); //           12. toString(); //           (22).toString();//          
      
      







最後の4つのコマンドでは、プリミティブがオブジェクトとどのように混同されているかを明確に見ることができます。結局、オブジェクトのように、ポイントを介してメソッドを呼び出します。 実際、これらのプリミティブはオブジェクトのようです。



変数の型をチェックすると、誤解がさらに複雑になります。たとえば、



 var v = null; typeof v;
      
      







応答で「オブジェクト」を取得します。



そして次のように書くと:

 var v = null; v instanceof Object;
      
      







最後の行の結果が「偽」になるため、頭に混乱が生じます。 つまり、変数vはオブジェクト型ですが、オブジェクトの型からは継承されません。 なんてこった!



最初に、typeof nullを使用したキャッチについて説明します。 この演算子は、オブジェクトのタイプを返します。 しかし、実際には、typeof演算子は、ハードコーディングされたテーブルから取得した文字列値を返します。これは、「for null-return」オブジェクト「。」 instanceof演算子は、指定されたデータ型に何かが属しているかどうかを確認します。 彼がこれをどのように行うかについては、次の記事で説明しますが、この場合、彼が正しく動作したことを保証します。nullプリミティブは決してObject型から継承されません。



さて、typeofとinstanceofを整理しましたが、メソッドはプリミティブで呼び出されます-オブジェクトのように! オブジェクトでない場合はどうすればいいですか?



ここにあります。 ラッパー関数(コンストラクター)などがあります(2番目の記事ですべてが明らかになります)。 これらは、すべてのプリミティブ(Number()、Boolean()、String())、およびその他のもの用です。 彼らの本質は、プリミティブからオブジェクトを作成することです。これには、このタイプのプリミティブを操作するための補助メソッドがあります。

たとえば、変数は次のように作成できます。



 Var num = new Number(23.456);
      
      







この場合、プリミティブ23.456からオブジェクトを取得します。

数値型の場合、Number()コンストラクターには補助メソッドtoPrecision()があります-数値の有効桁数を決定します。 たとえば、数値23.456が有効桁数4に設定されている場合、数値23.45が取得されます。

したがって、プリミティブをオブジェクトとして参照しようとすると:



 (23.456). toPrecision(4);
      
      







インタプリタは一時的にラップし、プリミティブにオブジェクトNumber new(23.456)を呼び出し、このオブジェクトが現在持っているtoPrecision()メソッドを呼び出します。 したがって、多くの人々は、JSのすべてがオブジェクトであると誤って信じています。



また、誤解を招き、何が起こっているのかを誤解している別の例もあります。 コードは次のとおりです。



 var str = 'str'; str.test = 'test'; //  ,   ,  console.log(str.test); //undefined
      
      







以前のように、strがオブジェクトであると考えた場合、新しいプロパティテストを覚えていない理由に驚くでしょう。 しかし、プリミティブをオブジェクトとして参照すると、一時的にString型のオブジェクトに変わることがわかりました。 ただし、操作が完了すると、このラッパーは表示されなくなり、新しいプロパティがテストされます。 それはすべて、魔法ではありません。



実際、オブジェクトにプリミティブをラップしている間、先を見て、継承のチェーン全体が構築されます(後で説明します)が、本質的には「入れ子人形」になります。



オブジェクト(数値(<プリミティブ>))。 いずれにしても、JSのオブジェクトの親はObjectになります。 オブジェクトでプロパティが呼び出されると、オブジェクトの1つでこのプロパティが見つかるか、未定義を返すまで、この「ネストされた人形」をすべて検索します。メソッドを検索すると、例外がスローされます。 したがって、プリミティブにはオブジェクトプロパティもあります。 プロトタイプ継承の仕組みとその微妙さについては、2番目の記事で説明します。



リリースしたい2番目の記事の陰謀については、関数コンストラクターに関連する別のポイントについて説明します。これは型変換です。 JSは強く型付けされた言語ではありません。 これは、変数が宣言された時点で、変数のタイプを示す義務がなく、さらに、プログラムの実行中に、任意の絶対タイプのデータをこの変数に格納できることを意味します。 また、たとえば、数学演算で文字列変数を使用したり、連結演算で数値を使用したりすることもできます。 例:



 var str = 'abc'; str+1; // 'abc1'
      
      







ここでは、タイプ番号-1のプリミティブがストリングプリミティブに変換されます。 オブジェクトでは、この機能はtoString()メソッドを呼び出すことで使用できます;型番号のオブジェクトには、型番号のプリミティブを返すvalueOf()メソッドがあります。 しかし、オブジェクトだけがメソッドを持つことができると言った。 それで、プリミティブをあるタイプから別のタイプに変換するプロセスで、プリミティブをオブジェクトにラップしますか? いいえ。 このメソッドは、コンストラクター関数がnew演算子なしでインタープリターによって呼び出される場合、明示的に呼び出されません。 新しい魔法の演算子の種類と、コンストラクタ関数がそれなしで呼び出されたときに何が起こるか、そして最後にこのコンストラクタ関数が何であるかについては、次の記事で説明します。 とりあえず、私の言葉を聞いてください-型変換はすぐに行われます-プリミティブからプリミティブへ。



これまでのところ、もちろん、答えよりも質問の方が多いですが、私を信じて、2番目の記事を読んだ後はすべてがより透明になります。 ここで基本的に興味をそそられ、いくつかの質問を投げかけました-いわば、私の心を興奮させました。 それでも、この記事から何かを学ぶことができます。

1.「JSのすべてがオブジェクトである」という従来の知恵にもかかわらず-これはそうではありません。プログラマーが利用できる6つのデータ型のうち、5つがプリミティブであり、1つだけがオブジェクト型を表すことがわかりました。

2.オブジェクトについて、これはキーと値のペアを含むそのようなデータ構造であることを学びました。 値がデータ型(およびオブジェクトのプロパティ)または関数(およびオブジェクトのメソッド)のいずれかになる場合。

3.しかし、プリミティブはオブジェクトではありません。 あなたはそれらをオブジェクトとして扱うことができますが(そしてこれはプリミティブがオブジェクトであると誤解を招きます)、しかし...

4.変数は、単純(リテラル)(var a = 'str')およびコンストラクター関数(ラッパー)(var a = new String( 'str'))の両方で宣言できます。 2番目の場合、プリミティブは取得しませんが、ラッパー関数String()によって作成されたオブジェクトを取得します。 (新しい魔法の演算子の種類と、後で見つけるコンストラクター関数とは)。

5.プリミティブ(新しい文字列( 'str'))でラッパーを作成することにより、オブジェクトのように操作できることがわかりました。 インタプリタがオブジェクトとして操作しようとしたときにプリミティブの周りに作成するのはこのラッパーですが、操作が完了すると破棄されます(したがって、プリミティブは割り当てられたプロパティを記憶できなくなりますa.test = 'test'-テストプロパティはラッパーとともに消えます)

6.オブジェクトには、オブジェクトの文字列表現を返すtoString()メソッドがあることを学びました(type number valueOf()の場合、数値を返します)。

7.連結操作または数学的操作を実行するとき、プリミティブはそのタイプを目的のタイプに再定義できることに気付きました。 これを行うには、それらのタイプのラッパー関数を使用しますが、new演算子は使用しません(str = String(str))(違いは何ですか?

8.そして最後に、typeofは固定されたテーブルから値を取得することを学びました(これはtypeof null //オブジェクトに基づく別の誤解がここから来る場所です)。



All Articles