前の記事では、プロトタイプの概念が紹介され、コンストラクター、プロトタイプオブジェクト、およびインスタンスの3人の親友の関係がJavaScriptに紹介されました。各コンストラクターには「ガーディアン」 - プロトタイプオブジェクトがあり、プロトタイプオブジェクトの中心にコンストラクターの「位置」もあります。 2つは恋をしていますが、この例はプロトタイプオブジェクトと「ひそかに恋をしている」ものであり、彼女はまた、プロトタイプオブジェクトの位置を心に留めています。
JavaScript自体は、オブジェクト指向の言語ではなく、オブジェクトベースの言語です。他のOO言語に慣れている人にとっては、ここに「クラス」の概念がないか、「クラス」と「インスタンス」の間に区別がないため、最初は少し不快です。「親クラス」と「サブクラス」の違いは言うまでもなく。それでは、JavaScriptのこれらのオブジェクトの山は、この方法でどのようにリンクされていますか?
幸いなことに、JavaScriptは、設計の開始時に「継承」の実装方法を提供しました。 「継承」を理解する前に、プロトタイプチェーンの概念を理解します。
プロトタイプチェーン
プロトタイプにはコンストラクターへのポインターがあることがわかっています。サブクラスのプロトタイプオブジェクトを新しいスーパークラス()の別のインスタンスに等しくするとどうなりますか?この時点で、サブクラスのプロトタイプオブジェクトにはスーパークラスのプロトタイプへのポインターが含まれており、スーパークラスのプロトタイプにはスーパークラスコンストラクターへのポインターも含まれています。 。 。このようにして、プロトタイプチェーンが形成されます。
特定のコードは次のとおりです。
function superclass(){this.name = "women"} superclass.prototype.saywhat = function(){return this.name + ":i`ma girl!"; } function subclass(){this.subname = "your Sister"; } subclass.prototype = new SuperClass(); subclass.prototype.subsaywhat = function(){return this.subname + ":i`ma beautiful girl"; } var sub = new subclass(); console.log(sub.saywhat()); // women:i`ma girl!プロトタイプチェーンを使用して、継承を実現します
上記のコードから、サブクラスがスーパークラスのプロパティと方法を継承していることがわかります。この継承された実装は、スーパークラスのインスタンスをサブクラスのプロトタイプオブジェクトに割り当てることです。このようにして、サブクラスのプロトタイプオブジェクトは、すべてのプロパティとメソッドを持ち、またスーパークラスのプロトタイプオブジェクトへのポインターを持つスーパークラスのインスタンスによって上書きされます。
プロトタイプチェーンを使用して継承を実装するときに注意する必要があるものがいくつかあります。
継承後のコンストラクターの変更に注意してください。ここでは、サブクラスのプロトタイプがスーパークラスをポイントするため、サブクラスのサブポイントのコンストラクターがスーパークラスに向かっています。プロトタイプチェーンを理解するときは、最後にデフォルトのオブジェクトオブジェクトを無視しないでください。そのため、すべてのオブジェクトでToStringなどの組み込みメソッドを使用できます。
プロトタイプチェーンを介して継承を実装する場合、プロトタイプメソッドの文字通りの定義を使用することはできません。これにより、プロトタイプオブジェクト(前の記事でも紹介されている)が書き換えられます。
function superclass(){this.name = "women"} superclass.prototype.saywhat = function(){return this.name + ":i`ma girl!"; } function subclass(){this.subname = "your Sister"; } subclass.prototype = new SuperClass(); subclass.prototype = {//プロトタイプオブジェクトは、スーパークラス属性とメソッドを継承することができないため、ここで上書きされます。 }} var sub = new subclass(); console.log(sub.say what()); // typeRror:未定義は関数ではありませんインスタンス共有の問題。以前にプロトタイプとコンストラクターを説明するとき、私たちはかつて、参照型属性を含むプロトタイプがすべてのインスタンスで共有されることを紹介しました。同様に、「親クラス」プロトタイプの参照タイプの特性もプロトタイプで共有されます。プロトタイプの継承を通じて「親クラス」の参照型属性を変更すると、プロトタイプから継承された他のすべてのインスタンスが影響を受けます。これはリソースだけでなく、私たちが見たくない現象でもあります。
function superclass(){this.name = "women"; this.bra = ["a"、 "b"]; } function subclass(){this.subname = "your Sister"; } subclass.prototype = new SuperClass(); var sub1 = new subclass(); sub1.name = "man"; sub1.bra.push( "c"); console.log(sub1.name); // man console.log(sub1.bra); // ["a"、 "b"、 "c"] var sub2 = new subclass(); console.log(sub1.name); // woman console.log(sub2.bra); // ["a"、 "b"、 "c"]]注:ここに配列に要素を追加すると、スーパークラスから継承されたすべてのインスタンスが影響を受けますが、名前属性を変更した場合、配列は参照タイプであり、名前は原始型であるため、他のインスタンスには影響しません。
インスタンス共有の問題を解決する方法は?見下ろし続けましょう...
古典的な相続(コンストラクターの盗み)
プロトタイプを使用してオブジェクトのみを単独で定義することはめったにないと紹介したように、実際の開発では、プロトタイプチェーンのみを使用することはめったにありません。参照タイプを共有する問題を解決するために、JavaScript開発者は古典的な継承パターンを導入しました(一部の人々は借りたコンストラクター継承と呼ばれます)。その実装は、サブタイプコンストラクターのスーパータイプコンストラクターを呼び出すのが非常に簡単です。 JavaScriptが提供するCall()またはApply()関数を使用する必要があります。例をご覧ください。
function superclass(){this.name = "women"; this.bra = ["a"、 "b"];} function subclass(){this.subname = "your Sister"; //スーパークラスの範囲を現在のコンストラクターに割り当てて、superclass.call(this);} var sub1 = new subclass(); sub1.bra.push( "c"); console.log(sub1.bra); // ["a"、 "b"、 "c"] var sub2 = new subclass(); console.log(sub2.bra); // ["a"、 "b"]]superclass.call(this);この文は、スーパークラスコンストラクターの初期化作業がサブクラスのインスタンス(コンテキスト)環境で呼び出されることを意味します。そのため、各インスタンスにはBRA属性の独自のコピーがあります。
ただし、この実装方法はまだ完璧ではありません。コンストラクターが導入されるため、前の記事で説明したコンストラクターの問題にも直面しています。コンストラクターにメソッド定義がある場合、インスタンスのいずれにも個別の関数参照があります。私たちの目的はこの方法を共有することであり、SuperTypeプロトタイプで定義する方法は、サブタイプのインスタンスでは呼ばれません。
function superclass(){this.name = "women"; this.bra = ["a"、 "b"]; } superclass.prototype.saywhat = function(){console.log( "hello"); } function subclass(){this.subname = "your Sister"; superclass.call(this); } var sub1 = new subclass(); console.log(sub1.say what()); // typeRror:未定義は関数ではありませんプロトタイプオブジェクトとコンストラクターに関する以前の記事を読んだ場合、この問題を解決するための答えを既に知っている必要があります。つまり、前の記事のルーチンに従って「コンビネーションパンチ」を使用してください!
組み合わせ継承
組み合わせ継承は、プロトタイプチェーンとコンストラクターの利点を組み合わせ、それらを組み合わせて継承を実現する方法です。簡単に言えば、プロトタイプチェーンを使用して属性とメソッドを継承し、借りたコンストラクターを使用してインスタンス属性の継承を実装することです。これは、インスタンス属性共有の問題を解決するだけでなく、スーパータイプの属性と方法を継承することもできます。
function superclass(){this.name = "women"; this.bra = ["a"、 "b"]; } superclass.prototype.saywhat = function(){console.log( "hello"); } function subclass(){this.subname = "your Sister"; superclass.call(this); // SuperClassへの2回目の呼び出し} subclass.prototype = new SuperClass(); // SuperClass var sub1 = new Subclass()への最初の呼び出し; console.log(sub1.say what()); //こんにちは組み合わせ継承方法は、実際の開発に継承を実装するための最も一般的に使用される方法でもあります。この時点で、それはあなたの実際の開発ニーズを満たすことができますが、人々の完璧さの追求は無限であるため、このパターンについて「見つける」誰かが必然的にいます。あなたのパターンはスーパータイプのコンストラクターを2回呼びました! 2回。 。 。あなたはそれを作りましたか?これはパフォーマンスの損失の100倍の増幅ですか?
最も強力な反論は解決策を考え出すことですが、幸いなことに、開発者はこの問題の最良の解決策を見つけました。
寄生的な組み合わせ継承
この継承方法を導入する前に、まず寄生コンストラクターの概念を理解します。寄生コンストラクターは、上記の工場パターンに似ています。そのアイデアは、共通の機能を定義することです。この関数は、オブジェクトの作成を処理するために特別に使用されます。作成が完了した後、このオブジェクトを返します。この関数はコンストラクターに非常に似ていますが、コンストラクターは値を返しません。
function gf(name、bra){var obj = new object(); obj.name = name; obj.bra = bra; obj.saywhat = function(){console.log(this.name); } return obj;} var gf1 = new gf( "bingbing"、 "c ++"); console.log(gf1.say what()); // bingbing寄生性継承の実装は、寄生的なコンストラクターに似ています。特定のタイプに依存せず、特にオブジェクト継承プロセスを扱う「ファクトリ」関数を作成し、継承されたオブジェクトインスタンスを返します。幸いなことに、これは私たちが自分でそれを実装する必要はありません。 Dao Ge(Douglas)は、長い間実装方法を提供してくれました。
function object(obj){function f(){} f.prototype = obj; return new f();} var superclass = {name: "bingbing"、bra: "c ++"} var subclass = object(superclass); console.log(subclass.name); // bingbingシンプルなコンストラクターがパブリック関数で提供され、渡されたオブジェクトのインスタンスがコンストラクターのプロトタイプオブジェクトに割り当てられ、最終的にコンストラクターのインスタンスを返すことは非常に単純ですが、有効性は非常に良いですよね?この方法は後に「プロトタイプ継承」と呼ばれ、寄生性継承はオブジェクトのカスタムプロパティを強化することにより、プロトタイプに基づいて達成されます。
関数buildobj(obj){var o = object(obj); o.saywhat = function(){console.log( "hello"); } return o;} var superclass = {name: "bingbing"、bra: "c ++"} var gf = burtionobj(superclass); gf.say what(); // hello寄生性の継承は、プロトタイプでの機能の再利用の問題にも直面しているため、人々は再び構成要素を組み立て始め、寄生的な組み合わせ相続が生まれました。上記の基本的な実装方法に基づいて、次のとおりです。
//パラメーターは2つのコンストラクター関数enternitoBJ(sub、sup){//インスタンス継承を実装し、supertype var proto = object(sup.prototype)のコピーを取得します。 // proto instanceのコンストラクター属性を尊重するproto.constructor = sub; //作成されたオブジェクトをサブタイプのプロトタイプに割り当てましたsub.prototype = proto;} function superclass(){this.name = "women"; this.bra = ["a"、 "b"];} superclass.prototype.saywhat = function(){console.log( "hello");} function subclass(){this.name = "women"; this.bra = ["a"、 "b"];} superclass.prototype.saywhat = function(){console.log( "hello");} function subclass(){this.subname = "your Sister"; superclass.call(this);} enternitobj(subclass、superclass); var sub1 = new subclass(); console.log(sub1.say what()); //こんにちはこの実装は、スーパータイプへの2つの呼び出しを回避し、Subclass.Prototypeに不必要なプロパティを保存し、プロトタイプチェーンも維持します。この時点で、継承の旅は本当に終わりました。この実装は、最も理想的な継承実装方法にもなりました! JavaScriptの相続に関する人々の論争はまだ継続しています。 OOを支持する人もいれば、OOの特性を実現するためにJavaScriptで不必要な努力をすることに反対する人もいます。もしそうなら、少なくとも私はより深い理解を持っています!