js繼承——到繼承結束

說到繼承,其它語言裏可能有兩種: 接口繼承是繼承方法簽名,而實現繼承則繼承實際方法。ES函數沒有簽名,不能實現接口繼承,只支持實現繼承,而實現繼承主要依靠原型鏈。(這兩句話,說來輕鬆,理解來不易,且行且珍惜吧~)。函數

因此,理解原型鏈是掌握繼承的必要條件。一個原型對象等與另外一個類型的實例this

function Parent(){
  this.super = "parent";
  this.friends = ["A", "B", "C"];
}
Parent.prototype.getParentValue = function(){
  return this.super;
}
function Child(){
  this.sub = "Child";
}
Child.prototype = new Parent();
Child.prototype.getChildValue = function(){
  return this.sub;
}
var c1= new Child();
c1.getParentValue();  //"parent"
c1.getChildValue();   //"Child"
c1.constructor === Parent;  //true
c1.constructor === Child;   //false

var c2= new Child(); c2.friends.push("D"); c2.friends //["A", "B", "C", "D"] c1.friends //["A", "B", "C", "D"]

 

爲何demo.constructor ===Parent;呢? 由於demo.prototype指向Parent實例,而Parent.prototype.constructor指向Parent,所以demo.constructor繼承自 Parent.prototype,因此指向Parent;spa

使用原型練實現繼承:說明:不能用對象字面量建立原型方法,由於這樣會重寫原型鏈。prototype

       缺點:1.引用類型值的原型屬性會被全部實例共享,所以在構造函數中定義屬性,但經過原型繼承時,一個類型的實例會變成另外一個對象的原型。所以實例中的屬性就變成了如今的原型的屬性了。2.沒有辦法在不影響全部對象的狀況下,向超類型傳參。code

爲了解決引用類型帶來的問題——>借用構造函數(僞造對象、經典繼承 ):在子類型構造函數的內部調用超類型構造函數對象

function Parent(){
  this.friends = ["A", "B", "C"];
}
function Child(){
  Parent.call(this);
  this.age = 23;
}
var c1 = new Child();
var c2 = new Child();
c1.friends.push("D");
c1.friends   //["A", "B", "C", "D"]
c2.friends  //["A", "B", "C"]
c2.age   //23

借用構造函數的缺點:沒法避免構造函數模式的缺點,方法不能複用,並且超類原型中的方法,對於子類型是不可見的,因此只能統一使用構造函數模式。blog

爲了不這些缺點——>組合繼承(僞經典繼承):使用原型鏈實現對原型屬性和方法的繼承,而經過構造函數實現對實例屬性的繼承繼承

function Parent(name){
  this.name = name || "parent";
  this.friends = ["A", "B", "C"];
}
Parent.prototype.sayName = function (){
    return this.name;
}
function Child(name, age){
   Parent.call(this, name);
   this.age = age;
}
Child.prototype = new Parent();
var c1 = new Child("Tom", 34);
var c2 = new Child("Joe", 22);
c1.friends.push("D");
c2.friends   //["A", "B", "C"]
c1.sayName();   //"Tom"

組合繼承避免了原型鏈和借用構造函數的缺陷,融合了它們的優勢。成爲js中最經常使用的繼承模式。
此外還有幾種繼承模式——>原型式繼承:借用原型基於已有的對象建立新的對象,同時還沒必要所以建立自定義類型。接口

function object(o){
  function F(){}
  F.prototype = o;
  return new F();
}
var person = {
   name: "Tom",
   friends: ["A", "B", "C"]
};
var p1= object(person);
p1.name = "Marry";
p1.friends.push("D");
var p2 = object(person);
p2.name = "Joe";
p2.friends.push("E");

person.friends           //["A", "B", "C", "D", "E"]

object()函數中建立了一個臨時構造函數,並將傳入的對象做爲該構造函數的原型。至關於進行一次淺複製。和原型模式同樣,引用類型始終會被共享。其中ES5定義了Object.create()方法規範了原型繼承。原型鏈

所以,在不必興師動衆的建立構造函數,而只想讓一個對象與另外一個對象保持相似的狀況下,原型式模式是個不錯的選擇。

——>寄生式繼承:與原型式繼承緊密相關的一種思路,與寄生構造函數和工廠模式相似,即建立一個封裝繼承過程的函數,該函數在內部以某種方式來加強對象。

function createAnother(o){
  var clone = object(o);
  clone.sayHi = function(){
     return "HI";
  };
   return clone;
}
var person = {
   name: "Tom",
   friends: ["A", "B", "C"]
};
var p = createAnother(person);
p.sayHi()   //"HI"

在主要考慮對象而不是自定義類型和構造函數的狀況下,寄生式也是一種有用的模式,object()非必須,任何能返回新對象的函數都適用於此模式
組合模式是最經常使用的繼承模式,可是組合模式兩次調用超類型構造函數,

爲了解決這個問題——>寄生組合模式:使用構造函數繼承屬性,經過原型鏈的混成形式繼承方法;沒必要爲了指定子類型的原型而調用超類型的構造函數,咱們可使用寄生式繼承繼承超類型的原型,而後再將結果指定給子類型的原型。

function inheritPrototype(C, P){
 var prototype = object(P.prototype);
 prototype.constructor = C;
 C.prototype = prototype;
}
function Parent(name){
  this.name = name || "parent";
  this.friends = ["A", "B", "C"];
}
Parent.prototype.sayName = function (){
    return this.name;
}
function Child(name, age){
   Parent.call(this, name);
   this.age = age;
}
inheritPrototype(Child, Parent);
Child.prototype.sayAge = function(){
    return this.age;
};

組合繼承模式:集寄生模式和組合模式優勢於一身,是實現基於類型繼承最有效最理想的方式。

相關文章
相關標籤/搜索