JavaScript-プロトタイプチェーンと継承

8013 ワード

前文:JavaScriptでは、クラスの実装はプロトタイプ継承メカニズムに基づいており、プロトタイプチェーンは継承を実現する主要な方法として機能しています.
構造関数、プロトタイプ、インスタンス
JavaScriptの継承について話す前に、私達がまず知りたいのは構造関数、プロトタイプ、インスタンスの三つの関係です.下のコードを見てもいいです.
function A(){
  this.name="A";
}
var B=new A();
A.prototype.constructor===A; //true
B.__proto__===A.prototype;  //true
上のコードを通じて、構造関数、プロトタイプ、インスタンスの3つの結論を得ることができます.この3つの結論はJavaScriptで継承を実現する理論的基礎です.だから、各構造関数にはプロトタイプのオブジェクトがあります.プロトタイプオブジェクトには、立体構造関数を指すポインタが含まれています.実例はいずれもプロトタイプを指す内部ポインタを含んでいる.
プロトタイプチェーンの継承が実現しました.
三者の関係を見てから、もう一つの問題を考えます.もし原型のオブジェクトを別の構造関数の例に等しくしたら、結果はどうなりますか?下のコードを見てください:
  function C() {
    this.person = {
      name: "  ",
      age: 24,
    };
  }
  function A() {
  }

  A.prototype = new C();
  var B = new A();

  C.prototype.constructor === C;//true
  //① true C              C   

  A.prototype.constructor === A;
  //② fasle A           C   ,   falseA.prototype.__proto__ === C.prototype;
  //③ true A      C   , C        C      ,  A           C     

  B.__proto__ === A.prototype;
  //④ true B A   ,  B     A      

  B.__proto__.__proto__ === C.prototype;
  //⑤ true  ③、④     

  B.__proto__.__proto__.constructor === C
  //⑥ true  ①、⑤    
上のこれらを見て回るかもしれませんが、心を落ち着けて見てほしいです.前の3つの理論の基礎を照らし合わせてみてください.実はこれは数学の論理推理の公式のようです.原理と公式を知ったら、いろいろな複雑な関係を導き出すことができます.Cのプロトタイプが構造関数Dの例である場合と同様に、上記の関係は依然として幾重にもわたって進み、実例とプロトタイプのチェーンを構成しています.これがプロトタイプチェーンの基本概念です.しかし、プロトタイプのチェーンだけでは継承が実現できません.これも重要な問題があります.
  • 参照タイプ値のプロトタイプ属性は全てのインスタンスに共有されますが、これもまた、構造関数+プロトタイプパターンを使用してオブジェクトを作成することを最も推奨する理由であり、構造関数で属性を定義し、プロトタイプで共有する方法を定義します.プロトタイプを使って継承を実現すると、プロトタイプは実際に前回は別のタイプの例になりますので、上記のコードの中でAのプロトタイプはCの例ですので、自然Cのperson属性はAのプロトタイプの属性になります.このようにして、下の問題を引き起こします.上のコードを続けて、AのインスタンスDを作成しました.このときAの他の例Bのperson.nameを修正して、Dのperson.nameも変化していることを発見しました.BとDのperson属性はいずれもAのプロトタイプのperson属性をポインタで指しています.person属性は参照タイプですので、この属性は共有されています.
  •   var D = new A();
      B.person.name = "  ";
      console.log(D.person)
      D.person.name === "  " //true
    構造関数を借りて継承を実現します.
    上記では、プロトタイプチェーンのみを通して、存在する引用タイプの値を継承することによる問題を説明しましたが、この問題を回避する方法がありますか?もちろんあります.これは構造関数を借りて、時には偽造の対象や古典的な継承とも呼ばれます.実は考え方も簡単です.つまり、サブタイプのコンストラクタの内部で超タイプのコンストラクタを呼び出します.つまり、サブタイプのコンストラクタの中でコールコール()とappy()を呼び出します.上のコードを修正して、サブタイプAでコール方法を呼び出してCを継承します.そうすると、将来AのインスタンスBとDを作成する時に、構造関数Aを実行して、person属性のコピーを作成します.コードは以下の通りです
      function C() {
        this.person = {
          name: "  ",
          age: 24,
        };
      }
      function A() {
        C.call(this);
      }
    
      A.prototype = new C();
      var B = new A();
      var D = new A();
      B.person.name = "  ";
      D.person.name === "  " //true
    構造関数によって、プロトタイプチェーンを解決して、存在する参照タイプの値を受け継ぐ問題を実現できます.AのインスタンスBとDのperson属性は二つの異なるレプリカです.彼らのポインタは同じオブジェクトを指すのではなく、インスタンス属性の共有問題を発生しません.しかし同様に、BとDの方法も異なるコピーであり、関数の多重化を実現できず、メモリ空間も比較的に消耗します.これは構造関数を使ってオブジェクトを作成する問題と同じです.
    構造関数+プロトタイプチェーンを借りて継承を実現します.
    ここを見て、私たちは確かにオブジェクトを作成するのと同じように、構造関数とプロトタイプを組み合わせて継承を実現するために使用すると、プロトタイプ上の定義方法によって関数の多重化を実現することができますし、各インスタンスが自分の属性を持つことを保証できます.その背後の考え方はプロトタイプチェーンを使ってプロトタイプの属性と方法の継承を実現することです.構造関数を借りることによって、インスタンス属性の継承が直接コード化されます.
      function C(name) {
        this.name = name;
        this.colors = ["red", "blue", "green"];
      }
      C.prototype.sayName = function () {
        alert(this.name);
      }
    
      function A(name, age) {
        C.call(this, name);
        this.age = age;
      }
      A.prototype = new C();
      A.prototype.sayAge = function () {
        alert(this.age);
      }
    
      var B = new A("  ", 25);
      B.colors.push("black");
      alert(B.colors);// red,blue,green,black
      B.sayName(); //  
      B.sayAge(); //25
    
      var D = new A("  ", 30);
      alert(D.colors);//red,blue,green
      D.sayName(); //  
      D.sayAge(); //30
    構造関数+プロトタイプチェーンを借りて継承を実現することはJavaScriptにおいて最も一般的な継承モードであり、プロトタイプチェーンと借用構造関数の欠陥を回避し、それらの長所を融合させる.
    リボン:
    JavaScriptは主にプロトタイプチェーンで継承されています.プロトタイプチェーンの構築は,あるタイプのインスタンスを他のコンストラクタのプロトタイプに値付けすることによって達成される.このように、サブタイプは、スーパータイプのすべての属性と方法にアクセスすることができ、この点はクラスベースの継承と似ている.プロトタイプチェーンの問題は、オブジェクトインスタンスがすべての継承の属性と方法を共有するため、単独で使用するのには適していない.この問題を解決する技術は,構造関数,すなわち,サブタイプのコンストラクタの内部で超型コンストラクターを呼び出すことである.このようにして、各インスタンスが独自の属性を持つようにすることができ、また、構造関数モードのみを使用してタイプを定義することを保証することができる.最も多くの継承モードを使用することは、結合継承であり、このようなモードは、プロトタイプチェーン継承共有の属性と方法を使用して、構造関数を借りることによってインスタンス属性を継承する.