【JS】JavaScriptの6つの継承方式のまとめ


前言
JSの中の継承について、いつも完全にそのようなことを理解していないので、最近実習を探して、面接の中でほとんどJSの継承を聞きます.だから今日は特にJSの中のいくつかの継承方式を整理しにきます.
JS継承を実現する6つの方式
継承方式を言う前に、まず父のコードと約束の言い方を定義します.
//        ,    
function Animal (name) {
    //    ,   ,       
    //var name = 'Animal';//      
    //var arr = [1];      //      
    //function sleep(){}  //    (    )

  //     
  this.name = name || 'Animal';//      
  this.arr = [1];              //      
  this.sleep = function(){     //    (    )
    console.log(this.name + '    !');
  }
}
//     
Animal.prototype.eat = function(food) {
  console.log(this.name + '   :' + food);
};
一、プロトタイプチェーン継承
核心:親類の実例は子類の原型を担当して例を挙げます.関連している説明はコードの中で注釈で説明しました.
function Cat(){}    //    
Cat.prototype = new Animal();//  ,Cat.prototype={name:'Animal',arr:[1],sleep: f()}
Cat.prototype.name = 'cat';//   Cat    name   

var cat = new Cat();  //new     cat,    (  )  ,      
console.log(cat.name);  //cat
console.log(cat.eat('fish')); //cat   fish
console.log(cat.sleep());  //cat    
console.log(cat instanceof Animal); //true 
console.log(cat instanceof Cat); //true

//    
var cat2 = new Cat();// new   Cat   
cat.arr.push(2);
console.log(cat.arr);//[1,2]
console.log(cat2.arr);//[1,2]
利点:1.非常に純粋な継承関係、例はサブクラスの例であり、親タイプの例2.親タイプはプロトタイプの方法/プロトタイプの属性を追加して、サブクラスはすべて3にアクセスできます.簡単で、欠点を実現しやすいです.1.cat.arrを修正して、cat 2.arrも変更しました.プロトタイプのオブジェクトからの引用属性はすべてのインスタンスで共有されています.cat.arr.pushを実行する(2).まずcatを属性検索して、実例の属性を探しました.見つけられなかったので、原型のチェーンに沿って上に探し始めました.catの原型のオブジェクトが見つかりました.arr属性があります.そこでarrの末尾に2を挿入したので、cat 2.arrも変わった.2.サブクラスのインスタンスを作成する場合、親の構造関数に転送できません.
二、構造関数の方式を借りる
コア:親の構造関数を使用して、子クラスのインスタンスを強化します.親クラスの実例的な属性をコピーしたものと同じです.
function Cat(name){
  Animal.call(this,name);  //  
}

var cat = new Cat('Tom');//new    cat,cat={name:'Tom',arr:[1],function sleep: f()}
console.log(cat.name);//Tom
console.log(cat.sleep());//Tom    
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

//    
var cat2 = new Cat('Ford');
console.log(cat.sleep === cat2.sleep);//false
利点:1.第1の方法のサブクラスのインスタンスが親クラスの参照属性を共有する問題を解決した.2.サブクラスのインスタンスを作成すると、親クラスの構造関数に欠点を伝えることができる.1.インスタンスは親タイプのインスタンスではなく、サブクラスのインスタンス2のみである.親タイプのインスタンス属性と方法を継承することができ、親タイプのプロトタイプの属性と方法を継承することができない.3.関数多重化はできない.各サブクラスのインスタンスには、親タイプの実例関数のコピーがあり、性能に影響を与える.
三、コンビネーション継承(最も一般的)
コア:親の構造を呼び出すことにより、親の属性を継承し、参照の利点を保持し、親のインスタンスをサブプロトタイプとして使用することにより、関数多重例を実現する.
function Cat(name){
  Animal.call(this,name);//  
}
Cat.prototype = new Animal();//  
Cat.prototype.constructor = Cat;//           

// Test Code
var cat = new Cat('Tom');
console.log(cat.name);//Tom
console.log(cat.sleep());//Tom    
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
利点:1.第2の方法の欠陥を補って、実例的な属性/方法を継承してもいいし、プロトタイプの属性/方法を継承してもいいです.サブクラスの例でもあり、親クラスの例でもあります.引用属性共有問題が存在しません.4.サブクラスのインスタンスを作成すると、親クラスのコンストラクターに5を送ることができます.二つのインスタンスが生成されました.(サブクラスの例はサブタイプのプロトタイプのブロックをしました.)
四、原型式継承
コア:関数をカプセル化し、この関数はインスタンス属性を含まないオブジェクトを返し、オブジェクトを徐々に強化します.
P.S.ES 5のObject.creat()関数は、内部がプロトタイプで継承され、IE 9+がサポートされています.
例:
function object(o){  //  
//             ,                   ,               。     ,                   。
    function F(){}
    F.prototype = o;
    return new F();
}

var Animal = {
    name: 'Tom',
    food: ['meat']
};

var cat = object(Animal);//     cat  ,      ,    Animal
cat.name = 'Bob';
cat.food.push("fish");

var dog = object(Animal);//     cat  ,      ,    Animal
dog.name = 'Alice';
dog.food.push("beef");

console.log(Animal.food); //"meat","fish","beef"
利点:1.既存のオブジェクトから新しいオブジェクトを生み出し、カスタムタイプを作成する必要がない(この例では、Animalから派生したcatとdogの2つのオブジェクトは、本質的にはオブジェクトの複製である)欠点:1.親タイプの参照属性はすべてのインスタンスで共有される2.コードの再利用ができない(オブジェクトは現在作成されています.属性は現在追加されています.全部関数パッケージを行っていません.どうやって多重化しますか?)
五、寄生式継承
コア:寄生式継承とプロトタイプ継承は密接に関連していると考えられます.寄生式継承はプロトタイプ継承のために馬甲を着用しただけです.パッケージ相続のための関数を作成します.この関数は内部で何らかの形で対象を強化します.最後には本当にすべての仕事をしたように対象に戻ります.
例:
function createAnother(o){
    var clone = object(o);//             ,                   ( Object.create()   )
    clone.sayHi = function(){
        console.log("hi");
    };
    return clone;
}

var Animal = {
    name: 'Tom',
    food: ['meat']
};

var cat = createAnother(Animal);;
cat.sayHi();  //"hi"
利点:1.カスタムタイプとコンストラクタではなくオブジェクトを主に考慮する場合、寄生式継承は有用なモードである.
短所:1.寄生式でオブジェクト追加関数を継承すると、関数多重が実現できなくなり、効率が低下します.
六、寄生組合継承(望ましい)
コア:寄生方式により、サブタイプの原型オブジェクトに余分な父親タイプの実例的な属性がカットされました.このようにして、2回の父親類の構造関数を呼び出した時に、2回のインスタンスメソッド/属性を初期化しません.グループ継承の欠点を避けました.
例:
function Cat(name){
        Animal.call(this,name); //  
    }
    (function(){   //  
        //             
        var Super = function(){};
        Super.prototype = Animal.prototype;
        //          
        Cat.prototype = new Super();
        Cat.prototype.constructor = Cat;//           
    })();

    var cat = new Cat('Tom');
    console.log(cat.name); //Tom
    console.log(cat.sleep()); //Tom    
    console.log(cat instanceof Animal); // true
    console.log(cat instanceof Cat); //true
利点:1.親の構造関数を一回だけ呼び出して、サブタイプの原型オブジェクトの上に不必要な、余分な属性を作成することを避けました.プロトタイプチェーンはそのままにして、instance ofとisProttypeOf()の欠点を使用できます.実現は複雑です.
問題
寄生グループ継承において、即時実行関数部分を以下のように変換すると、結果は寄生グループ継承の結果と同じです.
function Cat(name){
        Animal.call(this,name); //  
    }
    Cat.prototype = Animal.prototype;
    Cat.prototype.constructor = Cat;

    var cat = new Cat('Tom');
    console.log(cat.name); //Tom
    console.log(cat.sleep()); //Tom    
    console.log(cat instanceof Animal); // true
    console.log(cat instanceof Cat); //true
この時、サブクラスの例は、親の構造関数の例としての属性/方法があります.サブタイプの原型オブジェクトには父の構造関数の例としての属性/方法がありませんが、父の原型オブジェクトの属性/方法があります.なぜこのような方式が使われていませんか?ここで答えを知ることができるようになりたいです.ありがとうございます.
参照リンク
  • http://www.cnblogs.com/humin/p/4556820.html
  • http://www.ayqy.net/blog/%E9%87%8D%E6%96%B0%E7%90%86%E8%A7%A3js%E7%9A%846%E7%A7%8D%E7%BB%A7%E6%89%BF%E6%96%B9%E5%BC%8F/
  • 「JavaScript高級プログラム設計」