JSはこの点を引き継ぐ


ECMAScriptが継承を実現する方法は一つではない.これはJavaScriptにおける継承メカニズムが明確に規定されているのではなく,模倣によって実現されているためである.これは、すべての継承の詳細が解釈プログラムによって完全に処理されていないことを意味する.必要に応じて適切な継承方法を決定できます.
テキストリンク
オブジェクトのなりすまし
コンストラクション関数はthisキーを使用してすべての属性とメソッドに値を割り当てます(つまり、クラス宣言のコンストラクション関数方式を使用します).コンストラクション関数は単なる関数であるため、ClassAコンストラクション関数をClassBのメソッドとして呼び出します.ClassBはClassAのコンストラクション関数で定義された属性とメソッドを受け取ります.
function ClassA(name) {
    this.name = name;
    this.sayName = function () {
        console.log(this.name);
    };
}

function ClassB(name,age) {
    this.classA = ClassA;
    this.classA(name);
    delete this.classA;
    this.age = age;
    this.sayAge = function(){
        console.log(this.age);
    }
}

var tom = new ClassA('Tom');
var jerry = new ClassB('Jerry',25);
tom.sayName();                         //'Tom'
jerry.sayName();                       //'Jerry'
jerry.sayAge();                        //25
console.log(tom instanceof ClassA);    //true
console.log(jerry instanceof ClassA);  //false
console.log(jerry instanceof ClassB);  //true
すべての新しいプロパティと新しいメソッドは、スーパークラスの関連プロパティとメソッドを上書きする可能性があるため、新しいメソッドのコード行を削除した後に定義する必要があります.
オブジェクトの偽装は多重継承を実現することができるClassAとClassBが存在する場合、ClassCはこの2つのクラスを継承したいと考えています.以下のようにします.
function ClassA(name){
    this.name = name;
    this.sayName = function (){
        console.log(this.name);
    }
}

function ClassB(age){
    this.age = age;
    this.sayAge = function(){
        console.log(this.age);
    }
}

function ClassC(name,age){
    this.method = ClassA;
    this.method(name);
    
    this.method = ClassB;
    this.method(age);
    delete this.method;
}

var tom = new ClassC('Tom',25);
tom.sayName();                       //'Tom';
tom.sayAge();                        //25
console.log(tom instanceof ClassA);  //false
console.log(tom instanceof ClassB);  //false
console.log(tom instanceof ClassC);  //true
この実装の欠点は、2つのクラスClassAとClassBが同じ名前の属性またはメソッドを持っている場合、ClassBは後のクラスから継承されるため、優先度が高いことである.この継承方法の流行により,ECMAScriptの第3版はFunctionオブジェクトに2つの方法,すなわちcall()とapply()を加えた.
call
callメソッドは古典的なオブジェクト偽装メソッドと最も似た方法である.最初のパラメータはthisのオブジェクトとして使用され、他のパラメータは関数自体に直接渡されます.
function sayName(prefix) {
    console.log(prefix + this.name);
};

var tom = {};
tom.name = "Tom";

sayName.call(tom, 'This is ');  //'This is Tom'
関数sayNameはオブジェクト外で定義されますが、thisも参照できます.callメソッド書き換えオブジェクト偽装
function ClassA(name){
    this.name = name;
    this.sayName = function(){
        console.log(this.name);
    }
}

function ClassB(name,age){
    //this.method = ClassA;
    //this.method(name);
    //delete this.method;
    ClassA.call(this,name);
    this.age = age;
    this.sayAge = function (){
        console.log(this.age);
    }
}

var tom = new ClassB('Tom',25);
tom.sayName();                       //'Tom'
tom.sayAge();                        //25
console.log(tom instanceof ClassA);  //false
console.log(tom instanceof ClassB);  //true
Callメソッドは、属性を使用してClassAを参照する方法に代わります.
apply
applyメソッドには2つのパラメータがあり、thisのオブジェクトと関数に渡すパラメータ配列として使用されます.
function sayName(prefex,mark) {
    console.log(prefex+ this.name+ mark);
};

var tom = {};
tom.name = 'Tom';

sayName.apply(tom, ['This is ','!']);  //'This is Tom!'
同様にapplyを使用してオブジェクトを書き換えることができます.
function ClassA(name){
    this.name = name;
    this.sayName = function(){
        console.log(this.name);
    }
}

function ClassB(name,age){
    ClassA.apply(this,arguments);
    this.age = age;
    this.sayAge = function (){
        console.log(this.age);
    }
}

var tom = new ClassB('Tom',25);
tom.sayName();                       //'Tom'
tom.sayAge();                        //25  
console.log(tom instanceof ClassA);  //false
console.log(tom instanceof ClassB);  //true
パラメータ配列は、スーパークラスのパラメータ順序とサブクラスのパラメータが完全に一致している場合にのみ伝達されます.
プロトタイプチェーン
prototypeオブジェクトはテンプレートであり、インスタンス化するオブジェクトはこのテンプレートに基づいており、prototypeオブジェクトの任意の属性と方法はこのクラスのすべてのインスタンスに渡され、プロトタイプチェーンはこの機能を利用して継承メカニズムを実現します.
function ClassA() {}
ClassA.prototype.name = 'Tom';
ClassA.prototype.sayName = function () {
    console.log(this.name);
};

function ClassB() {}
ClassB.prototype = new ClassA();
var tom = new ClassB();
tom.sayName();                       //'Tom'
console.log(tom instanceof ClassA);  //true
console.log(tom instanceof ClassB);  //true
ここでClassBのprototype属性をClassAというインスタンスに設定し,prototpye属性を1つずつ与えないようにする.ClassAを呼び出すときにパラメータは設定されません.プロトタイプチェーンではコンストラクション関数がパラメータなしであることを確認する必要があります.プロトタイプチェーンではinstanceofの結果も変化し,ClassAとClassBともにtrueを返した.
prototypeプロパティの再指定のため、サブクラスの新しいプロパティはprototypeが割り当てられた後に表示される必要があります.
function ClassA() {}
ClassA.prototype.name = 'Tom';
ClassA.prototype.sayName = function () {
    console.log(this.name);
};

function ClassB() {}
ClassB.prototype = new ClassA();
ClassB.prototype.age = 25;
ClassB.prototype.sayAge = function () {
    console.log(this.age);
};

var tom = new ClassA();
var jerry = new ClassB();
tom.sayName();                         //'Tom'
jerry.sayName();                       //'Tom'
jerry.name = 'Jerry';
tom.sayName();                         //'Tom'
jerry.sayName();                       //'Jerry'
jerry.sayAge();                        //25
console.log(tom instanceof ClassA);    //true
console.log(jerry instanceof ClassA);  //true
console.log(jerry instanceof ClassB);  //true
プロトタイプチェーンの欠陥は、クラスのprototypeが書き換えられるため、多重継承が実現できないことです.
ハイブリッド方式
オブジェクトの偽装の問題は構造関数方式を使用しなければならないが,プロトタイプチェーンを使用するとパラメータ付き構造関数は使用できないが,両者を結合してみることができる.コンストラクション関数のプロパティをオブジェクトで偽って継承し、prototypeをプロトタイプチェーンで継承する方法.
function ClassA(name) {
    this.name = name;
}
ClassA.prototype.sayName = function () {
    console.log(this.name);
};

function ClassB(name, age) {
    ClassA.call(this, name);
    this.age = age;
}
ClassB.prototype = new ClassA();
ClassB.prototype.sayAge = function () {
    console.log(this.age);
};

var tom = new ClassA('Tom');
var jerry = new ClassB('Jerry',25);
console.log(tom instanceof ClassA);                    //true
console.log(jerry instanceof ClassA);                  //true
console.log(jerry instanceof ClassB);                  //true
console.log(jerry.constructor === ClassA);             //true
console.log(ClassB.prototype.constructor === ClassA);  //true
ClassBコンストラクション関数ではオブジェクトでClassAのname属性を継承したと偽って、ClassAのsayNameメソッドをプロトタイプチェーンで継承し、プロトタイプチェーン継承方式を使用したため、instanceof動作方式は正常です.しかし、constructorプロパティには問題があります.各prototypeオブジェクトにはconstructorプロパティがそのコンストラクション関数を指していますが、ClassBインスタンスのコンストラクション関数はClassAを指しており、継承チェーンの乱れを招きます.constructorの指向は手動で変更できます.
function ClassA(name) {
    this.name = name;
}
ClassA.prototype.sayName = function () {
    console.log(this.name);
};

function ClassB(name, age) {
    ClassA.call(this, name);
    this.age = age;
}
ClassB.prototype = new ClassA();
ClassB.prototype.constructor = ClassB;
ClassB.prototype.sayAge = function () {
    console.log(this.age);
};

var tom = new ClassA('Tom');
var jerry = new ClassB('Jerry',25);
console.log(tom instanceof ClassA);                    //true
console.log(jerry instanceof ClassA);                  //true
console.log(jerry instanceof ClassB);                  //true
console.log(ClassA.constructor === ClassB);            //false
console.log(jerry.constructor === ClassA);             //false
console.log(ClassB.prototype.constructor === ClassA);  //false
直接継承プロトタイプチェーン
メモリを節約するために、ClassAインスタンスの作成を実行せずに、ClassBのプロトタイプをClassAのプロトタイプに直接向けることができます.
function ClassA(name) {
    this.name = name;
}
ClassA.prototype.sayName = function () {
    console.log(this.name);
};

function ClassB(name, age) {
    ClassA.call(this, name);
    this.age = age;
}
ClassB.prototype = ClassA.prototype;
ClassB.prototype.constructor = ClassB;
ClassB.prototype.sayAge = function () {
    console.log(this.age);
};

var tom = new ClassA('Tom');
var jerry = new ClassB('Jerry',25);
console.log(ClassA.prototype.hasOwnProperty('sayAge'));  //true
console.log(ClassA.prototype.constructor === ClassB);   //true
このような欠点は,プロトタイプチェーン指向を直接修正し,ClassBプロトタイプチェーンにおける属性についてもClassAに影響を及ぼすため,ClassAがsayAgeメソッドを持ち,ClassAのコンストラクタ属性がClassBであるということである.
ブローカーとして空のオブジェクト
プロトタイプチェーンを直接継承する欠点を解決するために,仲介として空のオブジェクトを利用することができる.
function ClassA(name) {
    this.name = name;
}
ClassA.prototype.sayName = function () {
    console.log(this.name);
};

function ClassB(name, age) {
    ClassA.call(this, name);
    this.age = age;
}

var fn = function(){};
fn.prototype = ClassA.prototype;
ClassB.prototype = new fn();
ClassB.prototype.constructor = ClassB;
ClassB.prototype.sayAge = function () {
    console.log(this.age);
};
console.log(ClassA.prototype.hasOwnProperty('sayAge'));  //false
console.log(ClassA.prototype.constructor === ClassB);    //false   
オブジェクトインスタンスは作成されましたが、空のオブジェクトがメモリをほとんど占めていないため、ClassBのプロトタイプを変更してもClassAには影響しません.
extendsメソッドにカプセル化
function extends(child,parent){
    var fn = function (){};
    fn.prototype = parent.prototype;
    child.prototype = new fn();
    child.prototype.constructor = child;
    child.super = parent.prototype;
}
JSの柔軟性により、継承をさまざまな方法で実現することができ、その原理と実現を理解することで、異なるシーンで適切な方法を選択することができます.