JavaScriptオブジェクト(3)——継承

18466 ワード

3.継承
多くのOO言語は、2つの継承方式をサポートしています.インターフェースの継承と継承の実現です.インターフェースは継承方法の署名のみを継承し、継承を実現するには実際の方法を継承します.関数に署名がないため、ECMAScriptではインターフェース継承ができません.ECMAScriptは継承のみをサポートしており、その実現は主にプロトタイプチェーンによるものです.
3.1プロトタイプチェーン
プロトタイプチェーンの継承には基本的なパターンがあります.
function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function(){
    return this.property;
}
function SubType(){
    this.property = false;
}
//  SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValues = function(){
    return this.prototype;
}

var instance = new SubType();
alert(instance.getSuperType());             //true
       ,    SubType       ,           ;       SuperType   。  ,           SuperType              ,           ,   SuperType   。     :instance  SubType   , SubType      SuperType   。 
注意:SubType.prototypeは書き換えられていますので、デフォルトのconstructorの属性がなくなり、instance.com nstructorは現在SuperTypeを指しています.
プロトタイプチェーンに存在する問題:プロトタイプ継承を実現する時、プロトタイプは実際に別のタイプの例となり、元のインスタンス属性は現在のプロトタイプの属性となり、プロトタイプ属性はすべてのインスタンスに共有され、ひいては参照タイプ値を含むプロトタイプが生成される.
function SuperType(){
    this.property = true;
    this.colors = ["red","blue"];
}
SuperType.prototype.getSuperValue = function(){
    return this.property;
}
function SubType(){
    this.property = false;
}
//  SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValues = function(){
    return this.prototype;
}

var instance1 = new SubType();
instance1.colors.push("green");
console.log(instance1.colors);      //"red,blue,green"

var instance2 = new SubType();
console.log(instance2.colors);      //"red,blue,green"  
この例のSuperType構造関数は、本の序文(参照の種類値)を含むcolors属性を定義している.SuperTypeの各例には、それぞれの配列を含むColors属性があります.SubTypeがプロトタイプチェーンを通してSuperTypeを継承すると、SubType.prototypeはSuperTypeの一例となり、自分のcolors属性を持つようになります.その結果、SubTypeのすべてのインスタンスがColors属性を共有することになる.
プロトタイプチェーンの第二の問題は、サブタイプのインスタンスを作成する際に、超タイプの構造関数にパラメータを伝達できないことである.実際には、すべてのオブジェクトインスタンスに影響を及ぼさずに、超タイプの構造関数にパラメータを伝達する方法がない.
プロトタイプチェーンの継承には以上の二つの問題がありますので、プロトタイプチェーンを単独で使うことはあまりありません.
3.2構造関数を借りる
基本的な考え:サブタイプのコンストラクタの内部で超タイプのコンストラクタを呼び出す.
function SuperType(){
    this.colors = ["red","blue"];
}
function SubType(){
    //   SuperType
    SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("green");
console.log(instance1.colors);      //"red,blue,green"

var instance2 = new SubType();
console.log(instance2.colors);      //"red,blue"    
構造関数を借りるとパラメータを伝えることができます.
function SuperType(name){
    this.name = name;
}
function SubType(name){
    //   SuperType
    SuperType.call(this,name);
}
var instance = new SubType("Nicholas");
console.log(instance.name);     //"Nicholas"
コンストラクタモードを借りると問題があります.関数多重を実現できません.サブタイプ知能は超タイプのコンストラクタ上の属性と方法を取得し、超タイプのプロトタイプ上の属性と方法を取得できない.
構造関数モードを借りると、実際にも単独で使うことは少ないです.
3.3グループ引継ぎ
組み合わせ相続:原型の鎖の額を構造関数に借用する技術を一つのブロックに結合し、両者の長さを発揮する継承モードを指す.アイデア:プロトタイプチェーンを使ってプロトタイプの属性と方法の継承を実現し、構造関数を借りることによってインスタンス属性の継承を実現します.
function SuperType(name){
    this.name = name;
    this.colors = ["red","blue","green"];
}

SuperType.prototype.sayName = function(){
    alert(this.name);
};

function SubType(name, age){
    SuperType.call(yhis,name);                      //     SuperType()
    this.age = age;
}

SubType.prototype = new SuperType();                //     SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
}
プロトタイプチェーンとプロトタイプ関数の欠陥を回避し、それらの利点を融合させ、Javascriptで最も一般的な継承モードとなる.また、instance ofおよびisProttypeOf()は、組み合わせ相続に基づいて作成されたオブジェクトを識別するためにも使用されることができる.
3.4原型式継承
基本的な考え方:原型によって既存のオブジェクトに基づいて新しいオブジェクトを作成することができます.
function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}
object()関数の内部では、まず一時的なコンストラクタを作成し、次にこのコンストラクタのプロトタイプとして伝来したオブジェクトを最後にこの一時的なタイプの例を返します.本質的には、object()は、その中に入ってきたオブジェクトに対して浅い複製を実行した.例は以下の通りです
var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.fiends.push("Barbie");

alert(person.friends);              //"Shelby,Court,Van,Rob,Barbie"
プロトタイプ継承は、オブジェクトが別のオブジェクトの基礎となる必要があります.このようなオブジェクトがあるなら、それをobject()関数に渡すことができます.そして、具体的な需要によって得られたオブジェクトを修正すればいいです.
ECMAScript 5は、Object.create()を追加することにより、プロトタイプ継承を規範化しました.この方法は2つのパラメータを受信する.1つは新しいオブジェクトのプロトタイプとして使用するオブジェクトと、新しいオブジェクトのために追加の属性を定義するオブジェクトである.一つのパラメータが入ってくる場合、Object.create()は、Object()の方法挙動と同じである.
var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.fiends.push("Barbie");

alert(person.friends);           //"Shelby,Court,Van,Rob,Barbie"
Object.creat()メソッドの2番目のパラメータはObject.defineProperties()メソッドの2番目のパラメータフォーマットと同じです.各属性は自分のディスクリプタによって定義されます.このように指定された属性はいずれも原型オブジェクト上の同名属性をカバーします.例えば、
var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person,{
    name:{
        value:"Greg"
    }
});
alert(anotherPerson.name);              //"Greg"
Object.creat()メソッドをサポートするブラウザには、IE 9+、Firefox 4+、Safari 5+、Opera 12+、Chromeがあります.
一つの対象を他の対象と同じように維持したいだけであれば、原型式継承は完全に可能です.ただし、参照タイプの値を含む属性は常に対応する値を共有し、原型モードを使用するようになる.
3.5寄生式引継ぎ
寄生式継承は原型継承につながる考え方です.寄生式継承の考えは寄生構造関数と工場モードと類似しています.すなわち、継承プロセスをパッケージ化するための関数だけを作成します.この関数は内部である種の方法でオブジェクトを強化し、最後には本当にすべての仕事をしたように対象に戻ります.
function createAnother(original){
    var clone = object(original);            //             
    clone.sayHi = function(){                //            
        alert("hi");
    };
    return clone;                            //      
}
この例では、createAnother関数は、新しいオブジェクトの基礎となるオブジェクトとしてパラメータを受信します.そして、このオブジェクトをobject()関数に渡し、その結果をcloneに与えます.また、cloneオブジェクトに新しい方法のsayHi()を追加し、最後にcloneオブジェクトに戻ります.
var person={
    name: "Nicholas",
    friends: ["Shelby", " Court", "Van"]
};

var anotherPerson = createAnother(person);
anotherPerson.sayHi();                      //"hi"
この例のコードは、personに基づいて新しいオブジェクト、anothersonを返します.新しいオブジェクトは、personのすべての属性と方法だけでなく、自分のsayHi()方法もあります.目的のタイプとコンストラクタではなくオブジェクトを主に考慮する場合には,寄生型継承も有用なモードである.前のモデルがモードを継承する時に使うobject()関数は必要ではありません.新しいオブジェクトに戻る関数はこのモードに適用されます.
寄生式継承を用いてオブジェクトに関数を追加すると、関数多重ができないため効率が低下します.この点は構造関数モードと類似している.
3.6寄生結合式継承
コンビネーション継承はJavaScriptの最も一般的な継承モードです.しかし、グループ継承は、どのような場合でも、二次超構造関数を呼び出します.一回はサブタイプの原型を作成する時、もう一つはサブタイプの構造関数の内部にあります.サブタイプは最終的にはスーパータイプのオブジェクトのすべてのインスタンス属性を含みますが、サブタイプのコンストラクタを呼び出したときにこれらの属性を書き換えなければなりません.グループ継承:
function SuperType(name){
    this.name = name;
    this.colors = ["red","blue","green"];
}

SuperType.prototype.sayName = function(){
    alert(this.name);
};

function SubType(name, age){
    SuperType.call(yhis,name);                      //     SuperType()
    this.age = age;
}

SubType.prototype = new SuperType();                //     SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
}
SuperTypeコンストラクターを初めて呼び出した時、SubType.prototypeは二つの属性を得ます.nameとcolors.これらはすべてSuperTypeの実例的な属性ですが、現在はSubTypeの原型の中にあります.SubTypeコンストラクタを呼び出すと、今度はまた新しいオブジェクトにインスタンス属性nameとcolorsを作成します.この2つの属性は原型の中の2つの同名の属性を遮断した.
寄生結合式継承は,結合継承における2回の超形式呼び出しの構造関数問題を解決した.寄生結合式継承は,構造関数を用いて継承し,プロトタイプ鎖の混成形式により継承した.その背後の基本的な考え方は、サブタイプのプロトタイプを指定するために超タイプのコンストラクタを呼び出す必要がないということです.私たちが必要なのは、超タイプのプロトタイプのコピーだけです.本質的には、寄生式継承を用いて超タイプの原型を継承し、結果をサブタイプの原型に指定します.
寄生結合式継承の基本パターン:
function inheritPrototype(subType,superType){
    var prototype = object(superType.prototype);     //    
    prototype.constructor = subType;                 //    
    subType.prototype = prototype ;                  //    
}
この関数は二つのパラメータを受け取ります.関数の内部では、最初のステップは超型プロトタイプのコピーを作成します.第二のステップは、作成したコピーにconstructor属性を追加し、元のモデルを書き換えることによって失われたデフォルトのconstrutor属性を補います.最後のステップは、新規作成したオブジェクト(すなわちコピー)をサブタイプのプロトタイプに割り当てます.このように、私たちはinheit Prottype()関数の文言を呼び出して、前の例のサブタイプのプロトタイプのための語句を置き換えることができます.寄生結合式継承:
function SuperType(name){
    this.name = name;
    this.colors = ["red","blue","green"];
}

SuperType.prototype.sayName = function(){
    alert(this.name);
};

function SubType(name, age){
    SuperType.call(yhis,name);  
    this.age = age;
}

inheritPrototype(SubType,SuperType);
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
}
この例の高効率化は、SuperType構造関数を一回だけ呼び出し、SubType.Prottotype上に不必要な、余分な属性を作成することを回避するために実行される.それと同時に、プロトタイプチェーンはまだ変わらないです.そのため、instance ofとisProttypeOf()を正常に使用することができます.開発者は寄生結合式継承が引用型の最も理想的な継承モデルと考えられている.
この記事はまとめを参照してください.JavaScriptプレミアムプログラム設計(第三版)