1. 程式人生 > >Js實現繼承的方法

Js實現繼承的方法

fine new eat mce sco clas 高級 裏的 xiaomi

原型的作用:1.將公共部分放入原型中,這樣構造出的多個實例對象的公共部分只會占用一個公共空間,實現數據共享和節省內存空間
2.通過原型實現繼承:構造函數模擬 "類"這個面向對象的概念,JS基於對象,基於構造函數的原型對象實現繼承

如何實現繼承?
1.改變原型對象的指向:將子類構造函數(B)的prototype指向父類構造函數(A)的一個實例化對象(a),那麽這個子類構造函數構造出的實例化對象
(b)就可以訪問父類(A)的屬性和方法
缺陷:由於B的prototype改變,那麽保存在原來的B的prototype裏的屬性和方法就無法訪問了,構造出的b無法獲得這些屬性和方法
解決方法:先進行原型指向的改變,再定義子類的原型屬性和方法,這樣子類後定義的原型屬性和方法就定義到了父類的實例對象中
 var Person = function(name,sex){
        this.name = name;
        this.sex= sex;
    }
    Person.prototype.eat = function () {
      console.log("吃");
    };

    var Student = function(score){
        this.score = score;
    };
    Student.prototype.study = function () {
      console.log("學習");
    };

    Student.prototype 
= new Person("xiaoming","man"); var stu = new Student(66); stu.eat(); //可以調用 stu.study() //報錯,由於改變了prototype指向,無法尋找到study這個方法

先改變原型指向,後定義原型方法:

 var Person = function(name,sex){
        this.name = name;
        this.sex= sex;
    }
    Person.prototype.eat = function () {
      console.log(
"吃"); }; var Student = function(score){ this.score = score; }; Student.prototype = new Person("xiaoming","man"); Student.prototype.study = function () { console.log("學習"); }; var stu = new Student(66); stu.eat(); //可以調用 stu.study(); //可以調用 console.log(Student.prototype); /* name:"xiaoming" sex:"man" study:? () 可以看出後定義的方法寫入了Student.prototype即這個new Person實例化對象中了 __proto__:Object 這個隱式原型指向Person.prototype,裏面有eat方法 */

如果使用這個方式實現多代繼承,那麽每一代都需要先改變原型指向,在定義原型屬性和方法。

如果使用這個方式實現多代繼承,那麽每一代都需要先改變原型指向,在定義原型屬性和方法。
在新的原型鏈中,原本子代的構造函數的prototype消失,新的prototype即是父代的一個實例化對象。
而子代實例對象的__proto__都指向其父代的這個實例化對象,原型鏈就成為實例化對象之間的指向關系,直到最高級祖先的構造函數的prototype

仍然存在的問題:改變子代prototype的指向,指向父代的一個實例對象,那麽這個實例對象的屬性和方法就已經被初始化了,即繼承過來的屬性和方法是已經確定的,無法在構建子代實例化對象時重新初始化這些繼承下來的屬性和方法

2.借用構造函數實現繼承
* 利用call方法或者apply方法借用父代的構造函數
* 在子代構造函數中添加
* 父代構造函數.call(this.父代形參列表)
* 同時也要在子代的形參列表中加入父代的形參列表
* 想當於在子代構造函數中也寫了父代構造函數中的定義屬性和方法的那些代碼
* 所以優點:可以在子代構造函數實例化對象時自己初始化父代的屬性和方法,不再是繼承到固定的實現和方法
* 所以缺陷:沒有在子代的原型和父代原型之間形成原型鏈,所以無法訪問父代原型裏的方法和屬性
  var Person = function (name) {
        this.name= name;
        this.say = function () {
          console.log("Hi,I am" + this.name);
        };
    };
    Person.prototype.eat = function () {
        console.log("吃");
    };
    Person.prototype.sex = "man";   //父代原型裏的屬性

    var Student= function (score,name) {
        this.score = score;
        Person.call(this,name);
        // Person.apply(this,[name]);
    };
    Student.prototype.test = function () {
      console.log("考試");
    };


    var stu1 = new Student(80,"小王");
    console.log(stu1.name);
    stu1.say(); //可以調用父代構造函數裏的的屬性和方法

    console.log(stu1.sex);  //undefined
    stu1.eat();     //報錯------>無法通過借用構造函數的方法繼承父類原型裏的屬性和方法

3.組合繼承:結合以上兩種方法:

*   1):改變prototype指向(這時不需要再new父代實例化對象時傳入參數),子代和父代之間形成原型鏈,可以繼承父代原型裏的屬性和方法
* 2):借用父代的構造函數,實現繼承父代構造函數內的屬性和方法,還可以在實例化子代時自己初始化這些屬性和方法
    var Person = function (name) {
        this.name= name;
        this.say = function () {
            console.log("Hi,I am" + this.name);
        };
    };
    Person.prototype.eat = function () {
        console.log("吃");
    };
    Person.prototype.sex = "男";   //父代原型裏的屬性

    var Student= function (score,name) {
        this.score = score;
        Person.call(this,name);
        // Person.apply(this,[name]);
    };
    Student.prototype = new Person();
    Student.prototype.test = function () {
        console.log("考試");
    };


    var stu2 = new Student(80,"小王");
    console.log(stu2.name);
    stu2.say(); //可以調用父代構造函數裏的的屬性和方法

    console.log(stu2.sex);
    stu2.eat();         //可以調用父代原型裏的屬性和方法

    console.log(stu2.score);
    stu2.test();            //當然可以正常調用子代的屬性和方法



Js實現繼承的方法