ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScriptのプロトタイプとプロトタイプチェーンを詳しく解説_基礎知識

JavaScriptのプロトタイプとプロトタイプチェーンを詳しく解説_基礎知識

WBOY
WBOYオリジナル
2016-05-16 16:14:321116ブラウズ

JavaScript のすべてのオブジェクトには、組み込みの属性プロトタイプがあります。JavaScript のオブジェクトのプロトタイプ属性の説明は次のとおりです。オブジェクト型のプロトタイプへの参照を返します。これは、prototype 属性が、現在のオブジェクトの親オブジェクトとして機能する別の JavaScript オブジェクトへの参照を保持していることを意味します。

コードをコピー コードは次のとおりです:

A.prototype = new B();

プロトタイプを理解するには、プロトタイプを継承と混同しないでください。 A のプロトタイプは B のインスタンスです。A が B のすべてのメソッドとプロパティを複製したことがわかります。 A は B のメソッドとプロパティを使用できます。ここでは、継承ではなくクローン作成に重点を置いています。このような状況が発生する可能性があります。A のプロトタイプは B のインスタンスであり、B のプロトタイプも A のインスタンスです。

引き続き以下の分析を読んでください:

プライベート変数と関数

関数内で定義された変数と関数は、外部へのインターフェイスが提供されていない場合、つまり変数と関数が関数に対してプライベートである場合、外部からアクセスすることはできません。

コードをコピー コードは次のとおりです:

<スクリプトタイプ="text/javascript">
関数ボックス(){
var color = "blue";//プライベート変数
var fn = function() //プライベート関数
{

}
}


このようにして、変数 color と fn は関数オブジェクト Box の外部からアクセスできなくなり、プライベートになります:
コードをコピー コードは次のとおりです:

var obj = new Box();
alert(obj.color);//未定義のポップアップを表示
alert(obj.fn);//上と同じ

静的変数と静的関数

関数が定義され、ドット「.」によって追加された属性と関数はオブジェクト自体からアクセスできますが、そのインスタンスにはアクセスできない場合、そのような変数と関数はそれぞれ静的変数と静的関数と呼ばれます。

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

<スクリプトタイプ="text/javascript">
関数 Obj(){};

Obj.num = 72;//静的変数
Obj.fn = function() //静的関数
{

}

alert(Obj.num);//72
alert(obj.fn の種類)//function

var t = new Obj();
alert(t.name);//未定義
alert(typeof t.fn);//未定義

インスタンス変数と関数

オブジェクト指向プログラミングでは、いくつかのライブラリ関数に加えて、オブジェクトの定義時に同時にいくつかのプロパティとメソッドを定義することが望まれます。これらはインスタンス化後にアクセスできます。

コードをコピー コードは次のとおりです:

<スクリプトタイプ="text/javascript">
関数 Box(){
This.a=[] //インスタンス変数
; This.fn=function(){ //インスタンスメソッド

}
}

console.log(typeof Box.a) //未定義
console.log(Box.fn の種類); //未定義

var box=new Box();
console.log(box.a のタイプ); //オブジェクト
console.log(box.fn のタイプ); //関数

インスタンス変数とメソッドの新しいメソッドとプロパティを追加します

コードをコピー コードは次のとおりです:

<スクリプトタイプ="text/javascript">
関数 Box(){
This.a=[] //インスタンス変数
; This.fn=function(){ //インスタンスメソッド

}
}

var box1=new Box();
box1.a.push(1);
box1.fn={};
console.log(box1.a); //[1]
console.log(box1.fn のタイプ); //オブジェクト

var box2=new Box();
console.log(box2.a); //[]
console.log(box2.fn のタイプ); //関数

A と fn は box1 では変更されますが、box2 では変更されません。配列と関数はどちらもオブジェクトであり参照型であるため、box1 のプロパティとメソッドは box2 のものと同じ名前ですが、ただし、これは参照ではなく、Box オブジェクトによって定義されたプロパティとメソッドのコピーです。

これは属性にとっては問題ではありませんが、メソッドにとっては大きな問題です。メソッドはまったく同じ機能を実行しますが、関数オブジェクトに 1000 のメソッドとインスタンスのメソッドがある場合、それぞれのインスタンスが 2 回コピーされるからです。何千ものメソッドのコピーを維持する必要がありますが、これは明らかに非科学的です。プロトタイプが誕生しました。

基本概念

私たちが作成するすべての関数には、オブジェクトへのポインターであるプロトタイプ属性があります。このオブジェクトの目的は、特定の型のすべてのインスタンスで共有できるプロパティとメソッドを含めることです。そして、prototypeはコンストラクタを呼び出して作成されるオブジェクトインスタンスのプロトタイプオブジェクトです。

プロトタイプを使用する利点は、オブジェクト インスタンスがそれに含まれるプロパティとメソッドを共有できることです。つまり、コンストラクターに定義オブジェクト情報を追加する代わりに、この情報をプロトタイプに直接追加できます。コンストラクターを使用する場合の主な問題は、各インスタンスで各メソッドを作成する必要があることです。

JavaScriptには、プリミティブ値とオブジェクト値の2種類の値があります。すべてのオブジェクトには、通常プロトタイプと呼ばれる内部プロパティ プロトタイプがあります。プロトタイプの値はオブジェクトまたは null にすることができます。その値がオブジェクトの場合、そのオブジェクトにも独自のプロトタイプが必要です。これにより、プロトタイプ チェーンと呼ばれる線形チェーンが形成されます。

意味

関数はコンストラクターとして使用できます。さらに、関数のみがプロトタイプ属性を持ち、アクセスできますが、オブジェクト インスタンスにはこの属性はなく、アクセスできない内部の __proto__ 属性のみが存在します。 __proto__ は、オブジェクト内の関連するプロトタイプへの暗号的なリンクです。標準によれば、__proto__ は公開されていません。つまり、プライベート プロパティですが、Firefox エンジンは、外部からアクセスして設定できるパブリック プロパティとして公開します。

コードをコピー コードは次のとおりです:

<スクリプトタイプ="text/javascript">
var ブラウザ = function(){};
Browser.prototype.run = function(){
alert("私は Gecko、Firefox のカーネルです");
}

var Bro = 新しいブラウザ();
Bro.run();

Bro.run() メソッドを呼び出すと、Bro にはそのようなメソッドがないため、その __proto__ にアクセスしてそれを見つけます。これが Browser.prototype であり、最終的に run() メソッドが実行されます。 (ここでは、通常の関数と区別するため、関数の最初の文字を大文字でコンストラクタを表します)

インスタンスを作成するためにコンストラクターが呼び出されるとき、インスタンスにはコンストラクターのプロトタイプを指す内部ポインター (__proto__) が含まれます。この接続は、インスタンスとコンストラクターのプロトタイプの間に存在します。インスタンスとコンストラクターの間には存在しません。コンストラクター。

コードをコピー コードは次のとおりです:

<スクリプトタイプ="text/javascript">
関数担当者(名前){ This.name=name;
}
person.prototype.printName=function() //プロトタイプ オブジェクト

{
アラート(this.name);
}

var person1=new Person('Byron');// オブジェクトをインスタンス化します

console.log(person1.__proto__);//人
console.log(person1.constructor);// 自分で試して、何が起こるか見てみましょう
console.log(Person.prototype);//プロトタイプ オブジェクト Person
へのポイント var person2=新しい人('フランク');


Person インスタンス person1 には name 属性が含まれており、そのプロトタイプを指す __proto__ 属性が自動的に生成されます。プロトタイプで定義されている printName メソッドにアクセスできます。これはおそらく次のようになります。

すべての JavaScript 関数には、プロトタイプ属性があります。この属性は、プロトタイプ オブジェクトであるオブジェクトを参照します。プロトタイプ オブジェクトは初期化時には空です。その中のプロパティとメソッドはカスタマイズでき、これらのメソッドとプロパティはコンストラクターによって作成されたオブジェクトに継承されます。

さて、ここからが問題です。コンストラクター、インスタンス、プロトタイプ オブジェクトの関係は何ですか?

コンストラクター、インスタンス、プロトタイプ オブジェクトの違い

インスタンスはコンストラクターを通じて作成されます。インスタンスが作成されると、そのインスタンスにはコンストラクター属性 (コンストラクターを指す) と __proto__ 属性 (プロトタイプ オブジェクトを指す) が含まれます。

コンストラクターにはプロトタイプ属性があり、これはプロトタイプ オブジェクトを指すポインターです。

プロトタイプ オブジェクト内には、コンストラクターを指すポインター (コンストラクター属性) もあります: Person.prototype.constructor = Person;

インスタンスは、プロトタイプ オブジェクトで定義されたプロパティとメソッドにアクセスできます。

ここで person1 と person2 はインスタンスであり、prototype はそれらのプロトタイプ オブジェクトです。

別の例:


<スクリプトタイプ="text/javascript">
Function Animal(name) //蓄積コンストラクター
{
This.name = name;//オブジェクトのプロパティを設定します
}

Animal.prototype.behavior = function() //基本クラスのコンストラクターのプロトタイプに動作メソッドを追加します {

alert("これは " this.name);
}

var Dog = new Animal("dog");//Dog オブジェクトを作成します

var Cat = new Animal("cat");//猫オブジェクトを作成します


Dog.behavior();//Dog オブジェクトを通じて Behavior メソッドを直接呼び出します

Cat.behavior();//「これは猫です」を出力


alert(Dog.behavior==Cat.behavior);//出力 true;


プログラムの実行結果から、コンストラクターのプロトタイプで定義されたメソッドが実際にオブジェクトを通じて直接呼び出すことができ、コードが共有されていることがわかります。 (Animal.prototype.behavior のプロトタイプ属性を削除して、まだ機能するかどうかを確認してください。) ここで、プロトタイプ属性は Animal オブジェクトを指しています。

配列オブジェクトインスタンス

配列オブジェクトの例を見てください。オブジェクト array1 を作成するとき、JavaScript エンジンにおける array1 の実際のオブジェクト モデルは次のようになります:

コードをコピー コードは次のとおりです:

var array1 = [1,2,3];

array1 オブジェクトの長さ属性値は 3 ですが、次のメソッドを通じて array1 に要素を追加できます:

コードをコピー コードは次のとおりです:

配列1.push(4);
Push メソッドは、array1 の __proto__ メンバーが指すメソッド (Array.prototye.push()) から取得されます。すべての配列オブジェクト ([] によって作成されたもの) には、プッシュ、リバース、その他のメソッドで同じオブジェクト (Array.prototype) を指す __proto__ メンバーが含まれているため、これらの配列オブジェクトはプッシュ、リバース、その他のメソッドを使用できます。

関数オブジェクトインスタンス

コードをコピーします コードは次のとおりです:
function Base() {
This.id = "ベース"
}

コードをコピーします コードは次のとおりです:
var obj = 新しいBase();

このコードの結果は何でしょうか? Javascript エンジンで表示されるオブジェクト モデルは次のとおりです:

新しいオペレーターは具体的に何をするのですか? それは実際には非常に単純で、3 つのことを行います。


var obj = {}; obj.__proto__ = Base.prototype
Base.call(obj);



プロトタイプチェーン

プロトタイプ チェーン: オブジェクトからプロパティまたはメソッドを呼び出すとき、オブジェクト自体にそのようなプロパティまたはメソッドがない場合、プロトタイプにプロパティまたはメソッドがない場合は、関連付けられたプロトタイプ オブジェクトに移動して探します。プロトタイプの関連付けに移動して、前のプロトタイプを検索し、それ以上ない場合は、Prototype...Prototype が未定義になるまで (オブジェクトのプロトタイプが未定義になるまで) Prototype.Prototype によって参照されるオブジェクトの検索を続けます。 )、いわゆる「プロトタイプチェーン」を形成します。

コードをコピーします

コードは次のとおりです: <スクリプトタイプ="text/javascript"> 関数形状(){
This.name = "形状";
This.toString = function(){
return this.name;
}
}
関数 TwoShape(){
This.name = "2 形状";
}
関数 Triangle(辺,高さ){
This.name = "三角形";
This.side = サイド;
This.height = 高さ;
This.getArea = function(){
return this.side*this.height/2;
}
}

TwoShape.prototype = new Shape();
Triangle.prototype = new TwoShape();

ここでは、コンストラクター Shape() を使用して新しいエンティティが作成され、オブジェクトのプロトタイプをオーバーライドするために使用されます。

コードをコピー コードは次のとおりです:

<スクリプトタイプ="text/javascript">
関数形状(){
This.name = "形状";
This.toString = function(){
return this.name;
}
}
関数 TwoShape(){
This.name = "2 形状";
}
関数 Triangle(辺,高さ){
This.name = "三角形";
This.side = サイド;
This.height = 高さ;
This.getArea = function(){
return this.side*this.height/2;
}
}

TwoShape.prototype = new Shape();
Triangle.prototype = new TwoShape();

TwoShape.prototype.constructor = TwoShape;
Triangle.prototype.constructor = Triangle;

var my = new Triangle(5,10);
My.getArea();
My.toString();//Triangle
My.constructor;//Triangle(side,height)

プロトタイプの継承

プロトタイプの継承: プロトタイプ チェーンの最後にあるのは、オブジェクト コンストラクターのプロトタイプ属性によって指定されるプロトタイプ オブジェクトです。このプロトタイプ オブジェクトはすべてのオブジェクトの祖先であり、すべてのオブジェクトが本質的に持つべき toString などのメソッドを実装します。 Function、Boolean、String、Date、RegExp などの他の組み込みコンストラクターのプロトタイプはすべてこの祖先から継承されていますが、それらはそれぞれ独自のプロパティとメソッドを定義しているため、子孫はそれぞれのクランの特性を示します。それらの特徴。

ECMAScript では、継承を実装する方法はプロトタイプ チェーンに依存することです。

コードをコピー コードは次のとおりです:

<スクリプトタイプ="text/javascript">
Function Box(){ //継承された関数はスーパータイプ(親クラス、基本クラス)と呼ばれます
This.name = "ジャック";
}

function Tree(){ //継承された関数はサブタイプ(サブクラス、派生クラス)と呼ばれます
This.age = 300;
}
//プロトタイプチェーンの継承を通じて、サブタイプのプロトタイププロパティに値を割り当てます
//new Box() はボックス構造内の情報とプロトタイプを Tree
に渡します Tree.prototype = new Box();//Tree は Box を継承し、プロトタイプを通じてチェーンを形成します

var ツリー = new Tree();
alert(tree.name);//ポップアップジャック

プロトタイプ チェーンの問題: プロトタイプ チェーンは非常に強力で、継承の実装に使用できますが、いくつかの問題もあります。そのうちの主なものは、参照型を含む値プロトタイプに由来します。参照型を含むプロトタイプ プロパティはすべてのインスタンスで共有されます。これが、プロパティがプロトタイプ オブジェクトではなくコンストラクターで定義される理由です。プロトタイプを介して継承が実装されると、プロトタイプは実際には別の型のインスタンスに戻ります。その結果、元のインスタンス プロパティがプロトタイプ プロパティになります。

サブタイプのインスタンスを作成する場合、パラメーターをスーパータイプのコンストラクターに渡すことはできません。実際、すべてのオブジェクト インスタンスに影響を与えずにスーパータイプのコンストラクターにパラメーターを渡す方法はないと言うべきです。プロトタイプに参照型の値が含まれるために今説明した問題と相まって、プロトタイプ チェーンだけが実際に使用されることはほとんどありません。

別の例:

コードをコピー コードは次のとおりです:

<スクリプトタイプ="text/javascript">
関数 人(名前)
{
This.name = name;//オブジェクトのプロパティを設定します
};

person.prototype.company = "Microsoft";//プロトタイプのプロパティを設定します
Person.prototype.SayHello = function() //プロトタイプメソッド
{
alert("こんにちは、" this.company の " this.name " です);
};

var BillGates = new Person("BillGates");//人物オブジェクトを作成します
BillGates.SayHello();//プロトタイプの内容を継承し、「こんにちは、Microsoft の BillGates です」を出力します

var Jobs = new Person("Jobs");
Jobs.company = "Apple";//プロトタイプの会社属性をカバーして、独自の会社属性を設定します
Jobs.SayHello = function()
{
alert("こんにちは、" this.name " like " this.company);
};
Jobs.SayHello();//属性とメソッドを自分でオーバーライドし、「Hi, Jobs like Apple」を出力します
BillGates.SayHello();//ジョブのカバレッジはプロトタイプには影響せず、BillGates は引き続き
を出力します

次のプロトタイプ チェーンの例を見てください:

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

<スクリプトタイプ="text/javascript">
関数 Year(){
This.value = 21;
}
Year.prototype = {
メソッド:function(){

}
};

関数 Hi(){

};
//Hi のプロトタイプ属性を Year
のインスタンス オブジェクトに設定します Hi.prototype = new Year();
Hi.prototype.year = 'Hello World';

Hi.prototype.constructor = こんにちは;

var test = new Hi();//Hi の新しいインスタンスを作成します

//プロトタイプチェーン
[こんにちはインスタンス] をテストします
Hi.prototype [年のインスタンス]
{年:'Hello World'}
年.プロトタイプ
に object.prototype
                                                                                                                                                                                                          


上記の例から、テスト オブジェクトは Hi.prototype と Year.prototype を継承するため、 Year のプロトタイプ メソッド メソッドにアクセスでき、インスタンスの属性値にアクセスできます。

__ptoto__ 属性

__ptoto__ 属性 (IE ブラウザではサポートされていません) は、インスタンスからプロトタイプ オブジェクトへのポインターであり、その機能は、コンストラクターのプロトタイプ属性コンストラクターを指すことにより、属性とメソッドにアクセスできます。プロトタイプでは。

JavaScript のオブジェクト インスタンスは、基本的に一連の属性で構成されます。これらの属性の中に、内部の目に見えない特別な属性 (__proto__) があり、この属性の値はオブジェクト インスタンスのみが持つプロトタイプを指します。ユニークなプロトタイプ。

コードをコピー コードは次のとおりです:

<スクリプトタイプ="text/javascript">
Function Box(){ //大文字、コンストラクター
を表す Box.prototype.name = "trigkit4";//プロトタイプ属性
Box.prototype.age = "21";
Box.prototype.run = function()//プロトタイプ メソッド
                                                                                   return this.name this.age '勉強中';
}
}
var box1 = new Box();

var box2 = new Box();
alert(box1.constructor);//構築属性、コンストラクタ自体を取得できます、
//関数はプロトタイプ ポインターによって配置され、コンストラクター自体を取得します


__proto__ 属性とプロトタイプ属性の違い

プロトタイプは関数オブジェクトの独自の属性です。

__proto__ は通常のオブジェクトの暗黙的な属性です。 new を使用すると、
が指すオブジェクトを指します。 __ptoto__ は実際にはエンティティ オブジェクトの属性ですが、プロトタイプはコンストラクターに属する属性です。 __ptoto__ は学習環境またはデバッグ環境でのみ使用できます。

プロトタイプモードの実行処理

1. まず、コンストラクター インスタンス内の属性またはメソッドを検索し、存在する場合はすぐに返します。

2. コンストラクターのインスタンスがない場合は、そのプロトタイプ オブジェクト内でそれを探します。存在する場合は、すぐに戻ります
。 プロトタイプオブジェクトの


コードをコピーします コードは次のとおりです:
<スクリプトタイプ="text/javascript">
Function Box(){ //大文字、コンストラクター
を表す Box.prototype.name = "trigkit4";//プロトタイプ属性
Box.prototype.age = "21";
Box.prototype.run = function()//プロトタイプ メソッド
                                                                                   return this.name this.age '勉強中';
}
}

var box1 = new Box(); alert(box1.name);//trigkit4、プロトタイプの値

box1.name = "リー";
alert(box1.name);//リー、原則を入力してください

var box2 = new Box();

alert(box2.name);//trigkit4、プロトタイプ値はbox1

によって変更されていません


コンストラクターの


コードをコピーします コードは次のとおりです:
<スクリプトタイプ="text/javascript">
関数 Box(){ This.name = "ビル";
}

Box.prototype.name = "trigkit4";//プロトタイプ属性
Box.prototype.age = "21"; Box.prototype.run = function()//プロトタイプメソッド

{
return this.name this.age '勉強中';
}

var box1 = new Box();
alert(box1.name);//Bill、プロトタイプの値

box1.name = "リー";

alert(box1.name);//リー、原則を入力してください




まとめると、次のように整理しましょう:


コードをコピー コードは次のとおりです:

<スクリプトタイプ="text/javascript">
関数 person(){};

person.prototype.name = "trigkit4";
person.prototype.say = function(){
alert("こんにちは");
}

var p1 = new Person();//prototype は p1 と p2 のプロトタイプ オブジェクトです
var p2 = new Person();//p2 はインスタンス化されたオブジェクトであり、内部 __proto__ 属性を持ち、Person のプロトタイプを指します

console.log(p1.prototype);//未定義、このプロパティはオブジェクトであるためアクセスできません
console.log(person.prototype);//人
console.log(Person.prototype.constructor);//プロトタイプ オブジェクト内には、コンストラクター
を指すポインター (コンストラクター属性) もあります。 console.log(p1.__proto__);//この属性はプロトタイプのプロトタイプオブジェクトへのポインタです
p1.say();//インスタンスはプロトタイプ オブジェクトで定義されたプロパティとメソッドにアクセスできます


工場出荷時モード

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

function createObject(名前,年齢){
var obj = 新しいオブジェクト();
Obj.name = 名前;
Obj.age = 年齢;
obj を返します;
}

ファクトリ パターンは、インスタンス化されたオブジェクトの大量の重複の問題を解決しますが、別の問題があります。それは、それらがどのオブジェクトのインスタンスであるかを把握することが不可能であるということです。
コンストラクター メソッドを使用すると、インスタンス化の繰り返しの問題が解決されるだけでなく、オブジェクトの識別の問題も解決されます。

コンストラクター メソッドとファクトリ パターンの使用の違いは次のとおりです。

1. コンストラクター メソッドは、作成されたオブジェクト (new Object()) を表示しません。
2. このオブジェクトにプロパティとメソッドを直接割り当てます
3. return ステートメントはありません

コンストラクターが使用され、newconstructor() が使用されると、new Object() がバックグラウンドで実行されます。 関数本体のこれは、 new Object()
によって生成されたオブジェクトを表します。

1. プロパティがコンストラクターのインスタンス内にあるかプロトタイプ内にあるかを判断するには、`hasOwnProperty()` 関数を使用できます。

2. コンストラクター属性を使用してリテラルが作成される方法は、インスタンスを指すのではなく、オブジェクトを指すようになります。コンストラクターの作成方法は逆です。
なぜオブジェクトを指すのでしょうか? Box.prototype = {}; なので、この書き方では実際に新しいオブジェクトが作成されます。
関数が作成されるたびに、そのプロトタイプも同時に作成され、このオブジェクトはコンストラクター属性
も自動的に取得します。 3. インスタンス メソッドの場合、異なるインスタンス化には異なるメソッド アドレスがあり、一意です
4. プロトタイプメソッドの場合、アドレスは共有されます

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。