JavaScript實現繼承

如下內容均基於本人對《JavaScript高級程序設計》第三版6.3小節的理解app

先看一下父類

function Animal(name) {
    var name = name;             //'私有(受保護)'成員,只容許在父類的構造函數中賦值
    this.food = undefined;       //'公有'成員

    //引用類型的成員
    this.birthday = {            
      year: undefined
    };

    //構造函數中的方法,打印一些基本信息
    this.greeting = function() {
      console.log('Hi, my name is {' + name + '} I like eat {' + this.food + '} and my birth year is {' + this.birthday.year + '}');
    };

    //原型中的方法,將構造函數中的非函數成員以JSON格式打印
    Animal.prototype.briefInfo = function() {
      var brief = {
        name: name,
        food: this.food,
        birthday: this.birthday
      };
      console.log(JSON.stringify(brief));
    };
  }

方式一:原型鏈繼承

實現方式:子類的原型指向父類的實例函數

子類:測試

function Dog() {}
Dog.prototype = new Animal();        //原型指向父類的實例

測試:this

var dog1 = new Dog();
dog1.food = 'shit';
dog1.birthday.year = 2015;

var dog2 = new Dog();
dog2.food = 'bones';
dog2.birthday.year = 2016;

dog1.greeting();  //console: Hi, my name is {undefined} I like eat {shit} and my birth year is {2016}
dog2.greeting();  //console: Hi, my name is {undefined} I like eat {bones} and my birth year is {2016}

dog1.briefInfo();  //console: {"food":"shit","birthday":{"year":2016}}
dog2.briefInfo();  //console: {"food":"bones","birthday":{"year":2016}}

//以上,
//birthday是引用類型的屬性,因此dog1的birthday.year被dog2覆蓋了;
//沒法給dog1和dog2的name賦值

存在的問題:prototype

  • 引用類型的對象會被子類的全部實例共享(1.1)設計

  • 沒法在建立子類的實例時,給父類的構造函數傳遞參數(1.2)code


方式二:借用構造函數(僞造對象、經典繼承)

實現方式:在子類的構造函數中利用call(或者apply)方法執行父類構造函數(問題1.2解決),將執行對象設爲子類的this,至關於把父類構造函數中的成員拷貝了一份到子類(問題1.1解決)對象

子類繼承

function Dog(name) {
  Animal.call(this, name);
}

測試ip

var dog1 = new Dog('tom');
dog1.food = 'shit';
dog1.birthday.year = 2015;

var dog2 = new Dog('mike');
dog2.food = 'bones';
dog2.birthday.year = 2016;

dog1.greeting();  //console: Hi, my name is {tom} I like eat {shit} and my birth year is {2015}
dog2.greeting();  //console: Hi, my name is {mike} I like eat {bones} and my birth year is {2016}

//briefInfo是父類原型中的屬性,並無被繼承,如下語句會報錯
dog1.briefInfo();  //error: dog1.briefInfo is not a function
dog2.briefInfo();  //error: dog2.briefInfo is not a function

存在的問題:

  1. 父類原型中定義的屬性沒法被繼承


綜合方式一方式二,一種很明顯的方式呼之欲出了:

方式三:組合繼承

實現方式:結合原型鏈繼承和經典繼承

子類

function Dog(name) {
  Animal.call(this, name);    //經典繼承
}
Dog.prototype = new Animal();    //原型鏈繼承

測試

var dog1 = new Dog('tom');
dog1.food = 'shit';
dog1.birthday.year = 2015;

var dog2 = new Dog('mike');
dog2.food = 'bones';
dog2.birthday.year = 2016;

dog1.greeting();  //console: Hi, my name is {tom} I like eat {shit} and my birth year is {2015}
dog2.greeting();  //console: Hi, my name is {mike} I like eat {bones} and my birth year is {2016}
dog1.briefInfo();  //console: {"name":"tom","food":"shit","birthday":{"year":2015}}
dog2.briefInfo();  //console: {"name":"mike","food":"bones","birthday":{"year":2016}}

//終於獲得了預期的結果,算是較好的實現了繼承。
//可是,並不完美

存在的問題

  • 父類的構造函數被調用了兩次


爲了引出下一個繼承方式,先將函數的繼承放在一邊,看一下js中對象的繼承

(摘抄原文)

1. 原型式繼承

var person = {
  name: 'martin'
  firend: ['bob', 'steven']
};
  
function object(o) {
  function F() {};
  F.prototype = o;
  return new F();
}
var anotherPerson = object(person);    //antherPerson繼承了person

2. 寄生式繼承

function createAnother(original) {
  var clone = object(original);    //原型式繼承定義的方法
  //擴展對象
  clone.sayHi = function() {
    console.log('hi');
  }
  return clone;
}

回到正題,接下來介紹一顆 「銀彈」

方式四:寄生組合式繼承

實現方式:

  1. 利用經典繼承拷貝父類構造中的屬性到子類

  2. 利用原型式繼承建立一個繼承父類原型的對象

  3. 將該對象的constructor屬性指向子類的構造函數(寄生式繼承:擴展對象)

  4. 將子類的prototype指向該對象

子類

function Dog(name) {
  Animal.call(this, name);
}

function F() {}
var supProto = Animal.prototype;
F.prototype = supProto;
var subProto = new F();
subProto.constructor = Dog;
Dog.prototype = subProto;

測試

var dog1 = new Dog('tom');
dog1.food = 'shit';
dog1.birthday.year = 2015;

var dog2 = new Dog('mike');
dog2.food = 'bones';
dog2.birthday.year = 2016;

dog1.greeting();   //console: Hi, my name is {tom} I like eat {shit} and my birth year is {2015}
dog2.greeting();   //console: Hi, my name is {mike} I like eat {bones} and my birth year is {2016}
dog1.briefInfo();  //console: {"name":"tom","food":"shit","birthday":{"year":2015}}
dog2.briefInfo();  //console: {"name":"mike","food":"bones","birthday":{"year":2016}}
相關文章
相關標籤/搜索