JavaScript対象向けプログラミング——「対象継承」の注意点

10731 ワード

引き継ぐ
継承はインターフェースの継承と継承の実現に分けられます.インターフェースは継承方法の署名だけを継承して、継承を実現するには実際の方法を継承します.関数に署名がないため、ECMAScriptにはインターフェース継承がなく、プロトタイプチェーンによる継承のみが可能です.
原型チェーン
基本的な考え方は、プロトタイプチェーンを利用して、他の引用タイプの属性と方法を引継ぎます.
例えば:Person 2.prototypeはPerson 1.prototypeの継承であり、(「-」)は「指す」という意味である:
Person2.prototype - [[Prototype]] ---> Person1.prototype
Person2 - prototype ---> Person2
person2 - [[Prototype]] ---> Person2.prototype
//Person2.prototype   constructor

Person1.prototype - cunstructor ---> Person1
Person1 - prototype ---> Person1.prototype
person1 - [[Prototype]] ---> Person1.prototype
具体的な方法
具体的にどのように継承しますか?
function People(){}; //   
People.prototype.sayWorld = function(){
    return "World people"
};

function Person(){}; //   
Person.prototype = new People(); //          
Person.prototype.sayNation = function(){ //     prototype
    return "Chinese people"
};

var person = new Person(); //  
console.log(person.sayNation()); //Chinese people
console.log(person.sayWorld()); //World people
気をつけてください!!プロトタイプにメソッドのコードを追加するには、プロトタイプを置き換えるステートメントの後に置く必要があります!!!
もう一つ例を挙げます
function WorldPeople(){};
WorldPeople.prototype = {
    constructor: WorldPeople,
    color: "",
    say: function(){
        return "People in the Earth."
    },
    friends: ["Oliver","Alice","Troy"]
};

function Chinese(){};
Chinese.prototype = new WorldPeople();
Chinese.prototype.color = "yellow";
Chinese.prototype.say = function(){
    return "i am Chinese."
};

var person1 = new Chinese();

console.log(person1.friends.toString());
console.log(person1.color);
console.log(person1.say());    
/*
[Log] Oliver,Alice,Troy (repetition.html, line 163)
[Log] yellow (repetition.html, line 164)
[Log] i am Chinese. (repetition.html, line 165)
*/
原型と実例の関係を確定するinstanceofオペレータとinsPrototypeOf()方法を使えばいいです.
console.log(person1 instanceof Object); //true;
console.log(person1 instanceof WorldPeople); //true;
console.log(person1 instanceof Chinese); //true;

console.log(Object.prototype.isPrototypeOf(person1)); //true
console.log(WorldPeople.prototype.isPrototypeOf(person1)); //true
console.log(Chinese.prototype.isPrototypeOf(person1)); //true
慎重に定義する方法
プロトタイプにメソッドを追加するコードは、プロトタイプを置き換える文の後に必ず入れてください.
プロトタイプチェーンを通して継承を実現した場合、字面を使ってプロトタイプを作成することはできません.
プロトタイプチェーンの問題
  • は、すべてのインスタンスによって共有される参照タイプ値のプロトタイプを含む.
  • は、サブタイプのインスタンスを作成する際に、スーパータイプのコンストラクターにパラメータを伝達することができない.
  • 第一の問題について:
    function People(){}
    People.prototype.friends = ["Alice","Oliver"];
    
    function Person(){};
    Person.prototype = new People();
    
    var person1 = new Person();
    var person2 = new People();
    
    person1.friends.push("Troy");
    
    console.log(person1.friends);
    console.log(person2.friends); //      
    
    何か解決策がありますか?
    コンストラクタを借りる(推奨しない)
    「構造関数の借用」と呼ばれる技術や偽造の対象や古典的な継承.例えば:
    function People(){
        this.friends = ["Alice","Oliver"];
    }
    
    function Person(){
        People.call(this); //   People
    }
    
    //      Person.prototype = new People()
    
    var person1 = new Person();
    var person2 = new Person();
    
    person1.friends.push("Troy");
    
    console.log(person1.friends); //["Alice", "Oliver", "Troy"]
    console.log(person2.friends); //["Alice", "Oliver"]
    
    この方法の主な利点は,サブタイプのコンストラクタにおいて,超タイプのコンストラクタにパラメータを伝えることができることである.
    また、
    function SuperType(name){
        this.name = name;
    }
    
    function SubType(){
        SuperType.call(this,"Oliver"); //        SuperType,          
        this.age = 18;
    }
    
    var person = new SubType();
    console.log(person.name); //Oliver
    console.log(person.age); //18
    
    関数は多重化できないなどの問題のため、使用を推奨しません.
    コンビネーション継承(最も一般的なモード)
    疑似経典継承とも呼ばれる.例えば:
    //      
    function SuperType(name){
        this.name = name;
        this.colors = ["Blue","Red","Black"];
    }
    //     
    SuperType.prototype.sayName = function(){
        return (this.name);
    };
    
    //         
    function SubType(name,age){
        SuperType.call(this,name);//    
        this.age = age;//    
    }
    //         
    SubType.prototype = new SuperType();//    
    SubType.prototype.constructor = SubType;
    SubType.prototype.sayAge = function(){//    
        return (this.age);
    };
    
    var person1 = new SubType("Oliver",18);
    var person2 = new SubType("Troy",24);
    person1.colors.pop();
    console.log(person1.colors);
    console.log(person2.colors);
    console.log(person1.sayName() + person1.sayAge());
    console.log(person2.sayName() + person2.sayAge());
    
    /*
    [Log] ["Blue", "Red"] (repetition.html, line 255)
    [Log] ["Blue", "Red", "Black"] (repetition.html, line 256)
    [Log] Oliver18 (repetition.html, line 257)
    [Log] Troy24 (repetition.html, line 258)
    */
    
    最も一般的な方法.もう一つ例を挙げます
    function People(name,age){
        this.name = name;
        this.age = age;
        this.friends = [];
    }
    People.prototype.friendsList = function(){
        document.write(this.friends.toString());
    };
    
    function Person(name,age,color,job){
        People.call(this,name,age);
        this.color = color;
        this.job = job;
    }
    Person.prototype = new People();
    Person.prototype.constructor = Person;
    Person.prototype.sayInfo = function(){
        document.write(this.name + this.age + this.color + this.job);
    };
    
    var person1 = new Person("Oliver",18,"yellow","Hero");
    person1.friends.push("Alice");
    person1.sayInfo(); //Oliver18yellowHero
    person1.friendsList(); //Alice
    
    var person2 = new Person("Troy",24,"White","Fighter");
    person2.friends.push("Oliver","Islan");
    person2.sayInfo(); //Troy24WhiteFighter
    person2.friendsList(); //Oliver,Islan
    
    普段はこの方法で十分です.また、
    
    function Cars(name){
        this.name = name;
        this.hasColor = ["blue","black"];
    }
    Cars.prototype.sayName = function(){
        console.log(this.name);
    };
    
    function Car(name,color){
        Cars.call(this,name);
        this.color = color;
    }
    Car.prototype = new Cars();
    Car.prototype.constructor = Car;
    Car.prototype.sayColor = function(){
        console.log(this.color);
    };
    var benz = new Car("Benz-C200","Black");
    benz.hasColor.push("red");
    benz.sayName();
    benz.sayColor();
    console.log(benz.hasColor);
    var benz2 = new Car("Benz-C180","White");
    benz2.hasColor.push("white");
    benz2.sayName();
    benz2.sayColor();
    console.log(benz2.hasColor);    
    /*
    [Log] Benz-C200 (repetition.html, line 309)
    [Log] Black (repetition.html, line 319)
    [Log] ["blue", "black", "red"] (repetition.html, line 325)
    [Log] Benz-C180 (repetition.html, line 309)
    [Log] White (repetition.html, line 319)
    [Log] ["blue", "black", "white"] (repetition.html, line 330)
    */
    
    作成対象と継承対象を組み合わせて比較してみましょう.
    重要です
    重要です
    重要です
    //               -    
    function Person(name,age){
        this.name = name;
        this.age = age;
        this.friendsList = ["Alice","Islan"];
    }
    Person.prototype.friends = function(){
        console.log(this.friendsList.toString());
    };
    
    var person1 = new Person("Oliver",18);
    var person2 = new Person("Troy",24);
    person1.friendsList.pop();
    person1.friends(); //Alice
    person2.friends(); //Alice,Islan
    
    //    -    
    function Person(name,age){
        this.name = name;
        this.age = age;
        this.friendsList = ["Alice","Islan"];
    }
    Person.prototype.friends = function(){
        console.log(this.friendsList.toString());
    };
    function Info(name,age,job){
        Person.call(this,name,age);
        this.job = job;
    }
    Info.prototype = new Person();
    Info.prototype.constructor = Info;
    Info.prototype.sayJob = function(){
        console.log(this.job);
    };
    
    var person1 = new Info("Oliver",18,"Master");
    var person2 = new Info("Troy",24,"Hero");
    person1.friendsList.pop();
    person1.friends(); //Alice
    person2.friends(); //Alice,Islan
    person1.sayJob(); //Master
    person2.sayJob(); //Hero
    
    対照的に、継承属性は主にcallオペレータによる超型構造関数伝達パラメータに適用されていることが分かります.継承方法は字面量文法を使ってはいけません.
    以上
    プロトタイプ継承
    通常は一つの対象を他の対象と同じように維持したい場合、原型式継承は完全に可能です.対応する参照タイプの値の属性を共有します.
    文法は:
    function object(o){
        function F(){};
        F.prototype = o;
        return new F();
    }
    
    例えば:
    function object(o){
        function F(){};
        F.prototype = o;
        return new F();
    }
    
    var person = {
        name: "Oliver",
        friends: ["Alice","Islan"]
    };
    
    var anotherPerson = object(person);
    anotherPerson.name = "Troy";
    anotherPerson.friends.push("Ellen");
    
    var yetAnotherPerson = object(person);
    yetAnotherPerson.name = "Ellen";
    yetAnotherPerson.friends.push("Troy","Oliver");
    
    console.log(person.friends);
    
    また、
    function object(o){
        function F(){};
        F.prototype = o;
        return new F();
    }
    
    var person = {
        name: "Oliver",
        friends: ["Alice","Islan"]
    };
    
    var anotherPerson = object(person);
    anotherPerson.name = "Troy";
    anotherPerson.friends.push("Oliver");
    
    console.log(person.friends);["Alice", "Islan", "Oliver"]
    
    この方法は比較的簡単で,personとanothersonに類似した参照型の値の属性を維持し共有させたいだけである.
    寄生式継承(関数多重ができず効率が低下する)
    パッケージの継承プロセスの関数を作成します.
    function createAnotherObj(obj){
        var clone = obj;
        clone.sayHi = function(){
            console.log("hi");
        };
        return clone;
    }
    
    var person = {
        name: "Troy",
        friends: ["Alice"]
    };
    
    var anotherObj = createAnotherObj(person);
    anotherObj.sayHi();
    anotherObj.name = "Oliver";
    anotherObj.friends.push("Ellen");
    console.log(person.friends);
    console.log(anotherObj.friends); //      
    
    寄生結合式継承(望ましい継承モデル)
    基本的なロジックはまず超タイプのプロトタイプのコピーを作成します.コピーにconstructor属性を追加します.最後に副本をサブタイプの原型に割り当てます.例えば:
    function inheritPrototype(SubType,SuperType){
        var prototype = Object(SuperType.prototype);
        prototype.constructor = SubType;
        SubType.prototype = prototype;
    }
    
    function SuperType(name){
        this.name = name;
        this.color = ["red","yellow"];
    }
    SuperType.prototype.list = function(){
        console.log(this.color.toString());
    };
    
    function SubType(name,age){
        SuperType.call(this,name);
        this.age = age;
    }
    inheritPrototype(SubType,SuperType);
    SubType.prototype.sayName = function(){
        console.log(this.name);
    };
    
    var type1 = new SubType("Oliver",18);
    var type2 = new SubType("Troy",24)
    type2.color.pop();
    type1.list(); //red,yellow
    type2.list(); //red
    
    このようなモードをよく使うべきで、比較的に完備しています.