JSオブジェクト指向の魔法のプロトタイプ_JavaScript手法

WBOY
リリース: 2016-05-16 18:10:17
オリジナル
743 人が閲覧しました

JavaScript のオブジェクトのプロトタイプ属性は、オブジェクト タイプのプロトタイプへの参照を返すことができます。これを理解するには、まずオブジェクト型 (Type) とプロトタイプ (プロトタイプ) の概念を正しく理解する必要があります。
1 プロトタイプとは
JavaScript のオブジェクトのプロトタイプ属性は、オブジェクト型のプロトタイプへの参照を返すことができます。これを理解するには、まずオブジェクト型 (Type) とプロトタイプ (プロトタイプ) の概念を正しく理解する必要があります。
オブジェクトのクラス (Class) とオブジェクトのインスタンス (Instance) の間には「作成」関係があると前述しました。そのため、「クラス」をオブジェクトの特性のモデリングと見なし、オブジェクトをクラスと見なします。特性のこと、またはクラス (Class) はオブジェクトのタイプ (Type) です。たとえば、前の例では、p1 と p2 の型は両方とも Point です。JavaScript では、これは、instanceof 演算子によって確認できます。
p1 instanceof Point
p2 instanceof Point
ただし、Point はそうではありません。 p1 と p2 が唯一の型です。p1 と p2 は両方ともオブジェクトであるため、Object は Point よりも一般的なクラスであるため、Object と Point の間には派生関係があると言えます。ご存知のように、この関係は「継承」と呼ばれ、オブジェクト間の一般化された関係の特殊なケースでもあり、オブジェクト指向では不可欠な基本的な関係です。
オブジェクト指向の分野では、インスタンスとタイプだけが記述可能な抽象関係ではありません。JavaScript では、もう 1 つの重要な抽象関係がタイプ (Type) とプロトタイプ (プロトタイプ) です。この関係は、より高いレベルの抽象関係であり、タイプとインスタンスの間の抽象関係で 3 層のチェーンを形成します。
実生活では、何かが別のものに基づいているとよく言います。これら 2 つは同じタイプである場合もあれば、異なるタイプである場合もあります。 「瓢箪を追って瓢箪を描く」という慣用句ですが、ここでの瓢箪はプロトタイプであり、それをJavaScriptのプロトタイプで表すと「ladle.prototype = ある瓢箪」または「ladle.prototype = 新しい瓢箪」になります。 ()」。
プロトタイプをより深く理解するには、その設計パターンの 1 つであるプロトタイプ パターンを学習できます。このパターンの核心は、プロトタイプ インスタンスを使用して作成するオブジェクトのタイプを指定し、これらのプロトタイプをコピーして新しいオブジェクトを作成することです。 。 JavaScript プロトタイプはこのメソッドに似ています。
プロトタイプ パターンの詳細については、「デザイン パターン」を参照してください。この記事では説明しません。
タイプとインスタンスの関係とは異なり、プロトタイプとタイプの関係では、タイプは一度に 1 つのプロトタイプのみを持つことができる必要があります (インスタンスは明らかに一度に複数のタイプを持つことができます)。 JavaScript の場合、この制限には 2 つの意味があります。1 つは、特定の JavaScript タイプごとにプロトタイプが 1 つだけあることです。デフォルトでは、このプロトタイプは Object オブジェクトです (オブジェクト タイプではないことに注意してください)。 2 つ目は、このオブジェクトが属する型がプロトタイプ関係を満たす型チェーンである必要があるということです。たとえば、p1 のタイプは Point と Object であり、Object オブジェクトは Point のプロトタイプです。タイプが ClassA、ClassB、ClassC、および Object であるオブジェクトがある場合、これら 4 つのクラスは完全なプロトタイプ チェーンを形成する必要があります。
興味深いことに、JavaScript は型のプロトタイプの型を指定しません (これも非常に厄介なステートメントです)。そのため、任意の型 (通常はある種のオブジェクト) にすることができます。したがって、オブジェクト-型-プロトタイプ (オブジェクト) となります。これらの構造は JavaScript にさまざまな用途をもたらし、その中には賢いだけでなく美しさに満ちたものもあります。以下ではプロトタイプの使い方を中心に紹介します。

2 プロトタイプを使用するためのヒント
プロトタイプを使用するためのヒントを理解する前に、まずプロトタイプの特性を理解する必要があります。まず、JavaScript は各型 (Type) にプロトタイプ属性を提供します。この属性をオブジェクトに指定すると、このオブジェクトがこの型の「プロトタイプ」になります。これは、この型によって作成されたすべてのオブジェクトがこのプロトタイプの特性を持つことを意味します。 。さらに、JavaScript オブジェクトは動的であり、プロトタイプも例外ではありません。プロトタイプに属性を追加または削除すると、このタイプのプロトタイプが変更されます。この変更は、このプロトタイプによって作成されたすべてのオブジェクトに直接影響します。たとえば、

コードをコピーします コードは次のとおりです。

function Point(x,y) {
this.x = x;
this.y = y;
var p1 = 新しいポイント(1,2);
var p2 = 新しいポイント(3,4) ; 作成されたすべてのオブジェクト

a という名前の属性がオブジェクト型のプロトタイプに追加され、オブジェクト自体に a という同じ名前の属性がある場合、オブジェクトの属性 a にアクセスすると、オブジェクト自体の属性がプロトタイプになります。属性は「上書き」されますが、プロトタイプ属性は消えません。削除演算子を使用してオブジェクト自体の属性 a を削除すると、オブジェクトのプロトタイプ属性が再び表示されます。この機能を使用すると、オブジェクトのプロパティのデフォルト値を設定できます。例:
コードをコピー コードは次のとおりです。次のように:

<script> <br>関数 Point(x, y) <br>{ <br>if(x) this.x = x; this.y = y; <br>Point.prototype.x = 0; <br>var p1 = 新しい点 <br>var p2; 1,2); <br> </script>


上記の例では、プロトタイプを通じて Point オブジェクトのデフォルト値 (0,0) を設定しているため、p1 の値は (0, 0) で、p2 の値が (1,2) である場合、delete p2.x、delete p2.y; によって p2 の値を (0,0) に復元できます。さらに興味深い例を次に示します。


関数 classA()
{
this.a = 100;
this.c = 300; function( )
{
for(var each in this)
{
delete this[each]
}
}
}
classA.prototype = new classA( );

var a = new classA();
a.a *= 2;
a.c *= 2;
alert(a.a);
alert(a.c);
a.reset(); // a の値をデフォルト値に戻す🎜>alert(a.a );
alert(a.b);
これにより、上書きされなくなります。以下に例を示します。




コードをコピー


コードは次のとおりです。


<script> <br> 関数 Point(x, y) </div>{ <br>if(x) this.x = x; <br>if(y) this.y = y; <div class="codetitle">} <span>Point.prototype .x = 0; <a style="CURSOR: pointer" data="88210" class="copybut" id="copybut88210" onclick="doCopy('code88210')">Point.prototype.y = 0; <u></u>関数 LineSegment(p1, p2) </a>{ </span>//プライベートメンバー</div>var m_firstPoint = p1; >var m_lastPoint = p2; <div class="codebody" id="code88210">var m_width = { <br>valueOf : function(){return Math.abs(p1.x - p2.x)}, <br>toString : function(){return Math.abs (p1. x - p2.x)} <br>} <br>var m_height = { <br>valueOf : function(){return Math.abs(p1.y - p2.y)}, <br>toString : function() {return Math.abs(p1.y - p2.y)} <br>} <br>//getter <br>this.getFirstPoint = function() <br>{ <br>return m_firstPoint; >} <br>this.getLastPoint = function() <br>{ <br>return m_lastPoint; <br>} <br><br>this.length = { <br>valueOf : function(){return Math.sqrt (m_width* m_width m_height*m_height)}, <br>toString : function(){return Math.sqrt(m_width*m_width m_height*m_height)} <br>} <br>} <br>var p1 = 新しい点; 🎜>var p2 = new Point(2,3); <br>var line1 = new LineSegment(p1, p2); <br>lp.x = 100;誤って書き換えてしまった lp の値が破壊され、元の lp の値が破壊されて復元できなくなりました。 <br>alert(line1.getFirstPoint().x); //line1 でも。長さが変更されました<br></script>


this.getFirstPoint() を次のように書き換えます:




コードをコピーします


コードは次のとおりです。


this.getFirstPoint = function()
{
function GETTER(){};
GETTER.prototype = m_firstPoint;
return new GETTER();
}

を使用すると、この問題を回避し、m_firstPoint 属性の読み取り専用の性質を確保できます。
コードをコピー コードは次のとおりです:

<スクリプト>
関数 Point(x, y)
{
if(x) this.x = x;
if(y) this.y = y;
}
Point.prototype.x = 0;
Point.prototype.y = 0;

function LineSegment(p1, p2)
{
//私有成员
var m_firstPoint = p1;
var m_lastPoint = p2;
var m_width = {
valueOf : function(){return Math.abs(p1.x - p2.x)},
toString : function(){return Math.abs(p1.x - p2) .x)}
}
var m_height = {
valueOf : function(){return Math.abs(p1.y - p2.y)},
toString : function(){return Math .abs(p1.y - p2.y)}
}
//getter
this.getFirstPoint = function()
{
function GETTER(){};
GETTER.prototype = m_firstPoint;
新しい GETTER() を返します;
}
this.getLastPoint = function()
{
function GETTER(){};
GETTER.prototype = m_lastPoint;
新しい GETTER() を返します;
}

this.length = {
valueOf : function(){return Math.sqrt(m_width*m_width m_height*m_height)},
toString : function(){return Math. sqrt(m_width*m_width m_height*m_height)}
}
}
var p1 = 新しい点;
var p2 = 新しいポイント(2,3);
var line1 = 新しい LineSegment(p1, p2);
var lp = line1.getFirstPoint();
lp.x = 100; //小さな心は変更を書き終えたlpの値はありませんが、破壊的な初期の値はありません
alert(line1.getFirstPoint().x);
alert(line1.length); //line1.length不発行変更


では、1 つのオブジェクトを 1 つのタイプのテンプレートとして設定します。これは、そのタイプを例示することにより、オブジェクトがサブ本を作成するだけで、いつでもサブ本が変更されることに相当します。削除操作を使用すると、オブジェクト自身の同名プロパティが適用されているため、変更されていないプロパティがサブブックに影響を与える可能性がありますが、オリジナルのオブジェクトが変更される可能性があります。次のさらなる例:
复制代代码如下:

<script> <br>function Polygon() <br>{ <br>var m_points = []; <br><br>m_points = Array.apply(m_points, 引数); <br><br>関数 GETTER(){}; <br>GETTER.prototype = m_points[0]; <br>this.firstPoint = 新しい GETTER(); <br><br>this.length = { <br>valueOf : function(){return m_points.length}, <br>toString : function(){return m_points.length} <br>} <br><br> this.add = function(){ <br>m_points.push.apply(m_points, 引数); <br>} <br><br>this.getPoint = function(idx) <br>{ <br>return m_points[idx]; <br>} <br><br>this.setPoint = function(idx, point) <br>{ <br>if(m_points[idx] == null) <br>{ <br>m_points[idx] = ポイント; <br>} <br>else <br>{ <br>m_points[idx].x = point.x; <br>m_points[idx].y = point.y; <br>} <br>} <br>} <br>var p = new Polygon({x:1, y:2},{x:2, y:4},{x:2, y:6} ); <br>alert(p.length); <br>alert(p.firstPoint.x); <br>alert(p.firstPoint.y); <br>p.firstPoint.x = 100; //不小心写了它的值 <br>alert(p.getPoint(0).x); //私は实际に影響を与えません <br>delete p.firstPoint.x; //恢复 <br>alert(p.firstPoint.x); <br><br>p.setPoint(0, {x:3,y:4}); // セッター変更を通って实际の私有成员 <br>alert(p.firstPoint.x); //getter の值発行完了変更 <br>alert(p.getPoint(0).x); <br></script>

注意、上記の例は、プロトタイプを使用してオブジェクトを迅速に作成できる複数のサブ本を示しています。一般的な状況では、プロトタイプを使用して大量のオブジェクトを作成し、他の方法でオブジェクトをコピーする必要があります。
以下は例です:
复制代码代码如下:

<script> <br>var p1 = 新しいポイント(1,2); <br>var ポイント = []; <br>var PointPrototype = function(){}; <br>PointPrototype.prototype = p1; <br>for(var i = 0; i { <br>points[i] = new PointPrototype(); <br>//PointPrototype の構造関数は空関数であるため、その構造は直接構築される必要があります //p1 サブ本よりもはるかに多くなります。 <br><br><br>プロトタイプには、上記の使用方法の他にも、その独特の特性により、他の用途もあります。最も広く使用され、よく知られているのは、おそらく継承をシミュレートするために使用することです。次については 1 つのセクションで説明します。 <br><strong>3 プロトタイプの本質 <br></strong>プロトタイプの役割は上で述べました。ここで、ルールを通してプロトタイプの本質を明らかにします。 <br>プロトタイプは C の静的フィールドのように動作すると言います。プロトタイプのプロパティとしてプロパティを追加すると、このプロパティはこの型で作成されたすべてのインスタンスによって共有されますが、この共有は読み取り専用です。どのような場合でも、このプロパティは同じ名前の独自のプロパティで上書きすることのみが可能ですが、変更することはできません。つまり、オブジェクトが特定の属性を読み取るときは、必ず最初に自身のドメインの属性テーブルをチェックし、そのような属性が存在する場合はその属性を返します。それ以外の場合は、プロトタイプ ドメインを読み取り、その属性を返します。プロトタイプドメイン。さらに、JavaScript ではプロトタイプ フィールドで任意のタイプのオブジェクトを参照できるため、プロトタイプ フィールドを読み取った後に属性が見つからない場合、JavaScript はプロトタイプ フィールドがポイントするオブジェクトのプロトタイプ フィールドを再帰的に検索します。オブジェクトそのもの、またはサイクルが発生するまでは、次の図を使用してプロトタイプとオブジェクト インスタンスの関係を説明できます: <br>//TODO: <br><strong>4 プロトタイプ <br>より 上記の分析から、オブジェクトをプロトタイプとして使用して、安全に多数のインスタンスを作成できるプロトタイプを理解しました。これが、プロトタイプの本当の意味とその価値です。プロトタイプのこの機能を使用してオブジェクトの継承をシミュレートできることは後で説明しますが、プロトタイプは継承のシミュレートに使用され、プロトタイプの重要な値でもありますが、決してそのコアではないことを知っておく必要があります。つまり、JavaScript したがって、プロトタイプのサポートは、そのオブジェクトの継承を実装するためだけに使用されるわけではありません。プロトタイプの継承がなくても、JavaScript のプロトタイプのメカニズムは依然として非常に便利です。 </strong>プロトタイプは、型のコピーを構築するためのプロトタイプとしてオブジェクトのみを使用するため、大きな制限もあります。まず、型のプロトタイプ フィールドの値のコピーとして動作せず、参照コピーとして動作するため、「副作用」が生じます。プロトタイプ上の参照型のプロパティの値を変更すると (これもかなり厄介な説明です :P)、この型で作成されたすべてのインスタンスに完全に影響します。これはまさに必要なこと (特定のクラスのすべてのオブジェクトのデフォルト値の変更など) である場合もありますが、望ましくないこともあります (クラス継承の場合など)。 ><br><br><div class="codetitle"> <span>コードをコピーします<a style="CURSOR: pointer" data="65375" class="copybut" id="copybut65375" onclick="doCopy('code65375')"><u></u> コードは次のとおりです。</a></span> </div><script>function ClassA() <div class="codebody" id="code65375">{ <br>this.a=[]; <br>} <br>関数 ClassB() <br>{ <br>this.b=function(){}; <br>クラス B .prototype =new ClassA(); <br>var objB1=new ClassB(); <br>objB1.a.push(1,2,3); (objB2 .a); <br>//b のすべてのインスタンスの a のメンバーが変更されました。 !これは、この例で望んでいることではありません。 <br></script>


JavaScript 実装:
Java 言語では、オブジェクトはすべて java.lang.Object から継承され、Cloneable インターフェイスが実装されている限り、java.lang.Object は Clone メソッドを提供します。これは、Clone をサポートすることを意味します。それ以外の場合は、例外がスローされます。現時点では、JavaScript は非常に近いものになっています。ただし、Object は Clone メソッドをサポートしていません。ただし、将来的にはすべてのオブジェクトを作成できるように、JavaScript を通じて Clone メソッドを実装できます。クローン方式を実現しました。
JavaScript 自体は Clone メソッドを提供していないため、var a=new Object(); などのオブジェクト割り当てでは、このようなコード a と b は同じオブジェクトを指します。 object の場合は new を渡す必要があります。 このキーワードは実装に使用されるため、Clone の実装プロセス中に、内部でコンストラクター (constructor) CloneModel を定義し、その親オブジェクトを Clone アクティビティ自体に使用するオブジェクトとして指定しました。 this キーワードが使用されました。コンストラクター CloneModel に基づいてオブジェクトを 1 つずつ作成します。コンストラクター内にはコードがないため、実際には、新しく作成されたオブジェクトはすべての実装が親オブジェクト内にあることを示し、それがクローンを作成する必要があります。 。これまでのところ、コピーする必要があるオブジェクトを作成しましたが、すべての値は親オブジェクトを指しています。
JavaScript のオブジェクト指向アプローチでは、親オブジェクトの値が上書きされない場合、この時点でプロトタイプ パターンはオブジェクトの内部値を必要とすることを説明しました。 after Clone は関連しないはずであり、一度割り当てられている限り、objClone の値は親オブジェクトを指すのではなく、独自のメモリ空間にあります。この考慮に基づいて、 objClone[v]=objClone[v]; ステートメントは、親オブジェクトの値を上書きすることによってそれ自体のメモリにコピーします。

21.2.1 プロトタイプとは
JavaScript のオブジェクトのプロトタイプ プロパティは、オブジェクト型のプロトタイプへの参照を返すことができます。これを理解するには、まずオブジェクト型 (Type) とプロトタイプ (プロトタイプ) の概念を正しく理解する必要があります。 オブジェクトのクラス (Class) とオブジェクト インスタンス (Instance) の間には「作成」関係があると前述しました。したがって、「クラス」をオブジェクトの特性のモデル化と見なし、オブジェクトをクラスの特性の具体的な表現と見なします。 . 化、つまりクラス (Class) はオブジェクトの型 (Type) です。たとえば、前の例では、p1 と p2 の型は両方とも Point ですが、JavaScript では、instanceof 演算子を使用してこれを確認できます。 p1 インスタンスオブ ポイント p2 インスタンスオブ ポイント ただし、p1 と p2 の型はポイントだけではありません。 p1 と p2 は両方ともオブジェクトであるため、Object はその型でもあります。Object は Point よりも一般的なクラスであるため、Object と Point の間には派生関係があると言えます。この関係は「継承」と呼ばれます。 , これはオブジェクト間の一般化された関係の特殊なケースでもあり、オブジェクト指向では不可欠な基本的な関係です。 オブジェクト指向の分野では、記述可能な抽象関係のペアはインスタンスとタイプだけではありません。JavaScript では、もう 1 つの重要な抽象関係がタイプ (Type) とプロトタイプ (プロトタイプ) です。この関係は、より高いレベルの抽象関係であり、タイプとインスタンスの間の抽象関係を持つ 3 層のチェーンを形成します。

図 21.2 は、この関係を示しています。
、タイプとプロトタイプの間の関係。
実生活では、何かが別のものに基づいて作成されるとよく​​言います。これら 2 つは同じタイプである場合もあれば、異なるタイプである場合もあります。 「猫を描いて虎を描く」という慣用句では、ここでの猫はプロトタイプであり、トラは型です。これを JavaScript のプロトタイプを使用して表すと、「tiger.prototype = ある猫」または「tiger.prototype = new」になります。猫()"。 「プロトタイプ」は、自然界のものを記述する「分類」関係の一種です。その他の関係には、「継承」や「インターフェース」などがあります。一般に「相続」とは物事間の固有の派生関係を指しますが、「相続」で記述できる物事には強い相関関係(血縁関係)があります。 「インターフェース」は、機能の観点から物事の共通の特性を表します。 「プロトタイプ」は、物事間の「類似性」を説明する傾向があります。この観点から見ると、「プロトタイプ」は、物事の相関関係を記述する際の継承やインターフェースよりも広い意味を持ちます。 Java プログラマーの場合は、継承の観点から上記の例を検討してください。当然、「cat」を使用して「tiger」を継承することは不可能であり、「tiger」を使用して「cat」を継承することも不可能です。それらの関係を説明するには、それらの共通点をカバーする「抽象クラス」を作成する必要があります。そうでない場合は、それを「ネコ科」と呼びます。ただし、システム内で「猫」と「虎」だけを使用する必要がある場合、この冗長な「猫」は私にとって意味がありません。表現する必要があるのは、「虎」が「猫」に似ているということだけです。それだけです。ここでは、プロトタイプを使用することで、作成する必要のなかったタイプ「ネコ」を保存することができました。To understand prototype in depth, you can study one of its design patterns - prototype pattern. The core of this pattern is to use prototype instances to specify the types of objects to be created, and to create new objects by copying these prototypes. JavaScript's prototype is similar to this. For details on the prototype pattern, please refer to "Design Patterns", which is beyond the scope of this book. Note that the prototype pattern requires that a type can only have one prototype at a time (and an instance can obviously have multiple types at a time). For JavaScript, this restriction has two meanings. The first is that each specific JavaScript type has one and only one prototype. By default, the prototype is an Object object (note that it is not an Object type!) . The second is that all types of instances of this type must be type chains that satisfy the prototype relationship. For example, the types of p1 are Point and Object, and an Object object is the prototype of Point. If there is an object whose types are ClassA, ClassB, ClassC and Object, then these four classes must form a complete prototype chain, for example:
Example 21.4 Type chain of prototype relationship
function ClassA()
{
……
}
ClassA.prototype = new Object(); //This can be omitted
function ClassB()
{
……
}
ClassB.prototype = new ClassA(); //ClassB takes the object of ClassA as the prototype
function ClassC()
{
  …
}
ClassC.prototype = new ClassB(); //ClassC takes the object of ClassB as the prototype
var obj = new ClassC();
alert(obj instanceof ClassC); //true
alert(obj instanceof ClassB); / /true
alert(obj instanceof ClassA); //true
alert(obj instanceof Object); //true

Figure 21.3 briefly describes the relationship between them:
Figure 21.3 Type chain of prototype relationship
Interestingly, JavaScript does not specify the type of the prototype of a type (this is another very awkward statement), so it can be any type, usually some kind of object, so that object-type -The prototype (object) may form a ring structure, or other interesting topological structures. These structures bring a variety of uses to JavaScript, some of which are not only clever but also full of beauty. The following section mainly introduces the usage of prototype. <
関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のおすすめ
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!