ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript プロトタイプと継承
1. 関数の作成プロセス
プロトタイプ チェーンを理解する前に、まず作成プロセス中に関数が何を行うかを見てみましょう:
1 function A() {};
コード内でこのような空の関数を宣言する場合、JS 解析の本質は次のとおりです (表面的な理解が必要です):
1. ECMA によると、オブジェクトを作成します (コンストラクター属性と [[Prototype]] 属性を使用)。 [[プロトタイプ]] 属性は非表示であり、列挙可能ではありません
2. (名前とプロトタイプ属性を含む) 関数を作成し、プロトタイプ属性を通じて作成したばかりのオブジェクトを参照します
3. 変数 A を作成し、変数Aへの関数の参照
以下の図に示すように:
(図はすべて「参照」タイプであることに注意してください)
各関数の作成は、上記のプロセスを経ます。
2. コンストラクター
それでは、コンストラクターとは何ですか?
ECMAの定義によると
Constructorは新しく作成したオブジェクトを作成して初期化する関数です。
Constructorは新しいオブジェクトを作成して初期化するために使用される関数です。
新しいオブジェクトの作成と初期化を同時に行うために使用できる関数は何ですか?答えは、空の関数を含む任意の関数です。
結論としては、どの関数もコンストラクターになり得るということです。
3. プロトタイプ
前の空の関数作成図によると、各関数は作成時にプロトタイプ属性を自動的に追加することがわかります。これが関数のプロトタイプです。この図から、その本質が であることがわかります。オブジェクトへの参照 (このオブジェクトは一時的にプロトタイプ オブジェクトと呼ばれます)。
通常のオブジェクトと同じように、関数のプロトタイプ オブジェクトを操作できます。一緒に確認しましょう。
作成したばかりの空の関数の周囲にコードを追加します:
function A() {
this.width = 10;
this.data = [1,2,3];
this.key = "this is A";
}
A._objectNum = 0;//A の属性を定義する
A.prototype.say = function(){//A のプロトタイプ オブジェクトに属性を追加する
alter("hello world")
}
7 行目からコードの 9 では、関数のプロトタイプ オブジェクトに Say 属性を追加し、匿名関数を参照します。「関数の作成」プロセスによると、図は次のようになります。
(灰色の背景は、に基づいて追加された属性です。空の関数)
簡単に言えば、プロトタイプは関数の属性であり、関数の作成プロセス中に js コンパイラーによって自動的に追加されます。
それでは、プロトタイプの用途は何でしょうか?
まず、次のように new 演算子を理解してください:
1
2 var a1 = new A;
var a2 = new A;
これは、コンストラクターを使用してオブジェクトを作成する方法ですが、なぜ直接 var ではなくこの方法でオブジェクトを作成するのでしょうか。 a1 = {};?これには new の具体的な手順が含まれます。ここでの new 操作は 3 つの手順に分けることができます (例として a1 を作成します):
1. 新しいオブジェクトを作成し、それを変数 a1 に割り当てます: var a1 = {};
2. このオブジェクトを配置します [[Prototype]] 属性は関数 A のプロトタイプ オブジェクトを指します: a1.[[Prototype]] = A.prototype
3. 関数 A を呼び出し、これを 1 で作成したオブジェクト a1 に指します。オブジェクトを初期化します: A. apply(a1,arguments)
構造図は次のとおりです:
図からわかるように、オブジェクト a1 であっても a2 であっても、関数 A のプロトタイプ オブジェクトへの参照を保存する属性があります。これらのオブジェクトについては、関数のプロトタイプにいくつかの共通メソッドが見つかります。メモリスペースを節約します。
4. プロトタイプ チェーン
新しいオペレーターとプロトタイプの役割を理解した後、[[プロトタイプ]] とは何かを見てみましょう。そして、オブジェクトはこの参照に基づいて属性をどのように検索するのでしょうか?
js の世界では、各オブジェクトにはデフォルトで [[Prototype]] 属性があり、それが格納するアドレスはオブジェクトの作成時に js コンパイラーによって自動的に追加されます。 new 演算子の右側のパラメーターによって決定されます。var object1 = {}; の場合、var object1 = {}; は本質的に次と等しいため、object1 の [[Prototype]] は Object コンストラクターのプロトタイプ オブジェクトを指します。 var object = new Object(); (理由については、上記の new A の解析プロセスを参照してください)。
オブジェクトが特定の属性を探している場合、そのような属性が存在しない場合は、まずその属性をたどって、[[Prototype]] によって参照されるオブジェクトの検索を続けます。 [[Prototype]]...[[Prototype]] が未定義になるまで、[[Prototype]].[[Prototype]] オブジェクトの参照を検索し続けます (オブジェクトの [[Prototype]] が未定義になります)。 )
上の図に示されているように:
// a1.fGetName
を取得したいです。結果は、a1 オブジェクト自体には fGetName 属性がありません
//2. a1 の [[Prototype]] を見つけます。これは、対応するオブジェクト A.prototype であり、同時にそれを走査します
// 結果A.prototype にもこの属性がないということです
//3. A.prototype オブジェクトの [[Prototype]] を見つけて、それに対応するオブジェクト Object.prototype を指します
/ /その結果、Object.prototype には属性がありません。 have fGetName
//4. Object.prototype の [[Prototype]] 属性を検索しようとしましたが、結果は未定義を返します。これは a1.fGetName
の値です。簡単に言えば、[[Prototype]] を通じて保存されます。オブジェクトの別のオブジェクトへの参照。これを介して属性が上方向に検索されます。これがプロトタイプ チェーンです。
5. 継承
プロトタイプチェーンの概念により、継承が可能です。
1 関数 B() {};
このとき、B のプロトタイプ B.prototype が生成されます
プロトタイプ自体は Object オブジェクトであり、その中にどのようなデータが配置されているかがわかります
B.prototype は実際には {constructor: B, [ [Prototype]] : Object.prototype}
プロトタイプ自体は Object オブジェクトのインスタンスであるため、そのプロトタイプ チェーンは Object のプロトタイプを指します
B.prototype = A.prototype;//これは、 B から A へのプロトタイプ B のプロトタイプ チェーンのプロトタイプは A を指します。これを実装するにはどうすればよいですか?
1つ目は、プロトタイプチェーンの参照アドレスを変更することです
1 B.prototype.__proto__ = A.prototype;
ECMAには__proto__メソッドがありません。ffやchromeなどのjsインタプリタによって追加されます。これはEMCAに相当します。 [プロトタイプ]]、これは標準的な方法ではないので、標準的な方法を使用するにはどうすればよいですか?
new を操作するとき、実際にはインスタンス オブジェクトのプロトタイプ チェーンをコンストラクターのプロトタイプ アドレス ブロックにポイントするだけであることがわかっています。その後、次のように操作できます
1 B.prototype = new A();
結果は次のようになります。
A のインスタンスを生成し、それを B のプロトタイプに割り当てます。つまり、B.prototype はオブジェクト {width: 10, data: [1,2,3], key: "this is A", [ と同等です。 [Prototype]]: A.prototype}
このようにして、A のプロトタイプはオブジェクト属性 B.prototype.[[Prototype]] を通じて保存され、プロトタイプへのリンクが形成されます
ただし、オブジェクトのコンストラクターが生成されることに注意してくださいB にはコンストラクター属性がないため、B が変更されました。プロトタイプ チェーンから A.prototype を見つけて、constructor:A
var b = new B;
console.log(b.constructor);/ を読み出すことしかできません。 /output A
したがって、B 自体を手動で元に戻す必要があります
B.prototype.constructor = B;
//これで、B のプロトタイプは {width: 10, data: [1,2,3], key: " になりますこれは A", [[Prototype]] : A.prototype,constructor: B}
console.log(b.constructor);//出力 B
//同時に、B は A のカスタム属性の幅と名前を直接継承しますプロトタイプを通して
console.log(b.data) ;//output [1,2,3]
//これの欠点は
b.data.push(4);// データ配列を直接変更する (参考) プロトタイプの
var c = new B;
alert( c.data);//output [1,2,3,4]
//実際、定義したいのはプロトタイプ チェーンだけです。 B の A のカスタム プロパティ (プロトタイプではない)
// 継承を実行するにはどうすればよいですか?
//A にはカスタム属性が必要ないので、それらをフィルターで除外する方法を見つけることができます
// 新しい空の関数を作成できます
function F(){}
// 空のプロトタイプをポイントしますコンストラクター A Prototype
F.prototype = A.prototype;
//このとき、new オペレーションを使用して、B.prototype のプロトタイプ チェーンが F
のプロトタイプを指すようにします。 B.prototype = new F;
//このとき、B のプロトタイプは {[[Prototype]]: F.prototype} になります
//ここでの F.prototype は実際には単なるアドレスへの参照です
//ただし、B によって作成されたインスタンスのコンストラクターはポイントしますここでは、B.prototype
のコンストラクタ属性を表示して設定する必要があります。 B.prototype.constructor = B;
//このとき、Bのプロトタイプは{constructor: B, [[Prototype]]:となります。 F.prototype}
//このようにして、B から A へのプロトタイプ継承が実現されます。図は次のとおりです。赤い部分がプロトタイプ チェーンを表します。