javascript繼承之借用構造函數與原型

    在js中,關於繼承只有利用構造函數和原型鏈兩種來現實。之前所見到的種種方法與模式,只不過是變種罷了。javascript

借用構造函數java

// 一個動物類,包含名字和性別屬性
function Animal (name, sex) {
    this.name = name;
    this.sex = sex;    
    this.getName = function(){  
       return this.name;    
    };       
}

// Cat類繼承Animal基類,而且擁有額外的屬性
function Cat (name, sex, hasLegs) {
    this.hasLegs = hasLegs;
    Animal.apply(this, arguments);// 借用Animal的構造器
}

// Dog類繼承Animal基類,而且擁有與Cat類不同的額外的屬性
function Dog (name, sex, otherFeatures) {
    this.otherFeatures= otherFeatures;
    Animal.apply(this, arguments); // 借用Animal的構造器

}

     借用構造函數的優勢就是可以複用代碼;缺點就是它不能繼承基類的原型,以及部分代碼累贅。像Animal類中的getName方法,本該有一個就能夠了,可是每次調用其構造器都會開闢新的空間來存放這個方法。若是把這些共有的屬性或者方法放入原型鏈中,就不會須要每一個實例都有一個這樣的屬性或者方法,而是你們共用一個模板。app

構造函數與原型並用函數

// 從新定義動物類,
function Animal (name, sex) {
    this.name = name;
    this.sex = sex;    
}

// 提取公共的方法或者屬性放入原型鏈中
Animal.prototype.getName = function (){ return this.name;}

//Cat類不變,修改Cat的原型鏈,使其指向基類的原型
Cat.prototype = Animal.prototype;

//Dog類不變,修改Dog的原型鏈,使其指向基類的原型
Dog.prototype = Animal.prototype;

 測試代碼1測試

// 分別new一個對象
var cat = new Cat('咪咪', 'female', true),

     dog = new Dog('汪汪', 'male', null);

// 功能已實現
console.log(cat.getName(),dog.getName()); // 咪咪 汪汪

// 新的問題1
console.log(cat instanceof Cat, dog instanceof Cat); // true true 如今貓狗不分了

/*緣由是在改變各個具體子類的原型是,它們的構造器都指向了基類,它們擁有同一個構造器。
若是修改某個子類的原型constructor,必然會影響到其它子類*/

// 新問題2。若是如今Cat類的getName邏輯有變,不能修改基類的原型。現做出以下改動
function Cat (name, sex, hasLegs) {
    this.hasLegs = hasLegs;
    Animal.apply(this, arguments);
     // 新的邏輯
    this.getName = function (){
        return this.name+','+this.sex;
    }   
}

//可是這樣代碼又不能達到複用,由於每一個Cat實例都有一個getName方法。
/*如何解決上述問題呢,也許你想到了——原型【鏈】。突出個鏈字,鏈說明是一節一節的,若是
咱們在子類與基類原型中間再加一節,不就完事了麼*/

//定義一個空函數來作這個節點
function o (){}

// 讓‘空’節點指向基類的原型,Cat類再指向空節點
o.prototype = Animal.prototype;
Cat.prototype = new o();
// 重置Cat的構造器指針
Cat.prototype.constructor = Cat;

o.prototype = Animal.prototype;
Dog.prototype = new o();
Dog.prototype.constructor = Dog;

 完整的代碼this

    // 一個動物類,包含名字和性別屬性
    function Animal (name, sex) {
	  this.name = name;
	  this.sex = sex;
  
    }
    // 提取公共的方法或者屬性放入原型鏈中
    Animal.prototype.getName = function (){ return this.name;}
    
    function o (){}
    var oCat = new o();    
    // 修改Cat類的getName邏輯
    oCat.getName = function (){return this.name+','+this.sex;}

    o.prototype = Animal.prototype;
    Cat.prototype = oCat;
    //重值Cat構造器指針
    Cat.prototype.constructor = Cat;
    // 同上。而且這三行代碼的順序不能隨意改動
    o.prototype = Animal.prototype;
    Dog.prototype = new o();
    Dog.prototype.constructor = Dog;
	

    // Cat類繼承Animal基類,而且擁有額外的屬性
    function Cat (name, sex, hasLegs) {
	  this.hasLegs = hasLegs;
	  Animal.apply(this, arguments);
    }

    // Dog類繼承Animal基類,而且擁有與Cat類不同的額外的屬性
    function Dog (name, sex, otherFeatures) {
	  this.otherFeatures= otherFeatures;
	  Animal.apply(this, arguments);

    }

    var cat = new Cat('咪咪', 'female', true),

	dog = new Dog('汪汪', 'male', null);
    
    // 功能正常,代碼也達到進一步複用
    console.log(cat.getName(), dog.getName());
    // 如今貓是貓,狗是狗了
    console.log(cat instanceof Cat, dog instanceof Cat);
    // 兩個子類的構造器也是對的了
    console.log(cat.constructor, dog.constructor);

 如今彷佛完整了,但是好像仍是有些遺憾。如同被妹子拒了同樣:你人很好,咱們仍是作朋友吧。言外之意就是還沒好到讓妹子想跟你在一塊兒的程度。那麼哪裏不夠呢?如今只有兩個子類,若是有幾十個的話,仍是要作不少重複的工做;若是又有一個機械的基類,又要作一樣的事情。那麼,咱們能夠把這個繼承的方法寫成面向對象的形式麼?答案是:能夠滴。prototype

 

// 將繼承的實現細節用函數包裹起來,classPropers是須要覆蓋的屬性和方法

function inherit(classPropers){
     var o = function (){}, // 空函數用作空節點
          parent = this, // 這裏的this表明基類構造器
          child = function(){}, // 返回一個子類
          hasOwnConstructor = false; // 是否擁有本身的構造器
     if(typeof classPropers === 'object' 
        && classPropers.hasOwnProperty('constructor')){
         //若是有構造器屬性,則覆蓋構造器
         child = function (){
              classPropers.constructor.apply(this,arguments);    
         }
         hasOwnConstructor = true;
         
     }else{
         // 不然使用基類的構造器
          child = function(){
               parent.apply(this, arguments);
          }
     }
     o.prototype = parent.prototype; 
     child.prototype = new o(); 
     if(hasOwnConstructor){
        // 重置構造器指針
        child.prototype.constructor = classPropers.constructor
     }
     if(classPropers){
         /*$.extend是jQ函數,這裏再也不實現。若是classPropers與基類有相同的方法,則會‘覆蓋’
         基類的方法*/
         $.extend(child.prototype, classPropers);      
     }
     // 繼承基類的靜態方法,這樣子類還能夠被繼承
     $.extend(child, parent);
     child.__super__ = parent.prototype; // 以防萬一之後還要調用基類相同方法

     return child;

}         

 完整測試代碼2指針

// 一個動物類,包含名字和性別屬性
	function Animal (name, sex) {
	    this.name = name;
	    this.sex = sex;
  
	}
	Animal.prototype = {
		getName: function(){ return this.name;},
		getSex: function(){ return this.sex;}
	};
	function inherit(classPropers){
     var o = function (){}, // 空函數用作空節點
          parent = this, // 這裏的this表明基類構造器
          child = function(){}, // 返回一個子類
          hasOwnConstructor = false; // 是否擁有本身的構造器
     if(typeof classPropers === 'object'
        && classPropers.hasOwnProperty('constructor')){
         //若是有構造器屬性,則覆蓋構造器
         child = function (){
              classPropers.constructor.apply(this,arguments);   
         }
         hasOwnConstructor = true;
          
     }else{
         // 不然使用基類的構造器
          child = function(){
               parent.apply(this, arguments);
          }
     }
     o.prototype = parent.prototype;
     child.prototype = new o();
     if(hasOwnConstructor){
        // 重置構造器指針
        child.prototype.constructor = classPropers.constructor
     }
     if(classPropers){
         /*$.extend是jQ函數,這裏再也不實現。若是classPropers與基類有相同的方法,則會‘覆蓋’
         基類的方法*/
         $.extend(child.prototype, classPropers);     
     }
     // 繼承基類的靜態方法,這樣子類還能夠被繼承
     $.extend(child, parent);
     child.__super__ = parent.prototype; // 以防萬一之後還要調用基類相同方法
 
     return child;
 
	}  
	Animal.inherit = inherit;

	var Cat = Animal.inherit({sayHi:function(){console.log('喵喵...')}}),
	cat = new Cat('咪咪', '不告訴你');

	console.log(cat.getName(),cat.getSex());

  var Dog = Animal.inherit({
                constructor:function(name){ 
                  this.name = name;
                  console.log('我有本身的工廠(構造器)');
                }
            }),
  dog = new Dog('我爲本身代言');
  console.log(dog.getName(),dog.constructor);

  // 老虎小時候就是貓,不信,我有證據以下。
  var Tiger = Cat.inherit({constructor:function(){console.log('出來一聲吼啊!喵喵......咋變貓叫了呢?wuwu...')}}),
  tiger = new Tiger();

  tiger.sayHi();

 記得引用jQuery或者本身實現$.extend函數。對象

相關文章
相關標籤/搜索