總結繼承的幾種方式


簡單總結繼承的幾種方式

JavaScript作為一門弱類型的語言,本着精簡的原則,它取消了類的概念,只有對象的概念,

更是有萬物皆對象的說法。在基於類的面向對象方式中,對象(object)依靠類(class)來產生。

而在基於原型的面向對象方式中,對象(object)則是依靠構造器(constructor) 利用 原型(prototype)

構造出來的。而JavaScript語言正是如此,它是通過一種叫做原型(prototype)的方式來實現面向對象編程的。

它和其他的面向對象類編程語言一樣,只是它的實現方式不同而已,或者說他們采用了不同的面向對象設計哲學。

那么下面就讓我們來簡單總結一下繼承的幾種方式:

  1. 擴展原型對象實現繼承

構造函數有一個 prototype 屬性,指向的就是原型對象,通過給原型對象添加屬性和方法,讓構造函數的實例

都可以訪問到,從而實現繼承

function Animal(name,color,say){
  this.name = name;
  this.color = color;
  this.say = function(){
    console.log('喵喵喵');
  }
}

var cat = new Animal('cat','white');
// 如果給Animal的prototype屬性上添加個 cry 方法 ,那么實例對象 cat將也會有 cry方法 
Animal.prototype.cry = function(){
  console.log('嗚嗚嗚');
}
  1. 替換原型對象實現繼承 (常用類型)
  • 為什么會有該方式呢?
    其實上面擴展原型對象的方法已經很方便了,但是為什么我們還需要使用該方式呢?試想一下,
    當我們需要給構造函數的原型對象添加許多屬性和方法的時候,豈不是要寫很多冗余(重復)的代碼。例如上面的例子
    看下面代碼:

        // 我們要給 構造函數Animal 添加很多的方法 即:
        Animal.prototype.aaa = function(){};
        Animal.prototype.bbb = function(){};
        Animal.prototype.ccc = function(){};
        Animal.prototype.ddd = function(){};
        Animal.prototype.eee = function(){};
        ......  // 諸如這樣的話,代碼就顯得不那么優雅,高效了吧。
    
  • 實現方式?
    重新給構造函數的prototype屬性(原型對象)賦值,指向一個全新的對象,在這個對象中添加屬性和方法,注:
    一定要添加一個constructor屬性,並且指向構造函數本身 具體看代碼:

        Animal.prototype = {
          // 一定要指明constructor屬性,不然的話,會根據原型鏈查找一直到Object.prototype
          constructor : Animal;  
          saying : function(){},
          crying : function(){},
          doing : function(){}
         ......
        }
    
  1. 混入繼承
  • 混入繼承的使用場景:已知對象 o1, o2, 需要把 o1中的功能(屬性方法)拷貝到 o2 中去

  • 實現方式 :用 for...in... 對 o 進行遍歷(可以將混入繼承的模式封裝成函數),如下所示

      // target : 目標對象(接收數據的對象)
      // source : 接收對象 (數據從哪個對象中來)
      function mixin(target,source){
          for(var key in source){
            target[key] = source[key];
          }
          return target;
      }
    
  • 原理其實很簡單,jQuery中的$.extend 方法利用的就是混入繼承的原理

  1. 原型 + 混入繼承
  • 本質上就是對混入繼承的一次運用

  • 只不過目標對象為原型對象而已

          // 運用上面封裝的混入繼承的函數  將對象{a:10,b:20,c:function(){}}的功能考本到Animal原型對象上去
          mixin(Animal.prototype, {a:10,b:20,c:function(){}} );  
          // 還可以 這樣操作 給Animal構造函數 的原型對象添加一個extend方法,在該方法中調用mixin函數,這樣的話也可以實現
          Animal.prototype.extend = function(){
               mixin(Animal.prototype, source);
          }
          // 不過兩種方法沒有本質之差,看大家易於接受哪個了
    
  1. 經典繼承 —> 道格拉斯《JavaScript語言精粹》中提到的一種繼承模型
  • 實現的功能:已知一個對象 o1 需要創建一個新的對象 o2 ,這個新的對象需要繼承自對象 o1,代碼如下:

        // 可以將經典繼承 封裝成一個函數
        function create(o){
          function F(){};    // 創建一個構造函數
          F.prototype = o;   // 將F 的原型指向 對象 o;
          return new F();    // 將 構造函數的實例 返回出去,這樣的話 F的實例對象,就會繼承自 o
        }
        var o2 = create(o1);  // 即:o2 繼承於 o1 ;  o2.__proto__ = o1;
    
  • 使用場景 :要創建一個對象(不需要關心構造函數),新對象需要繼承自另一個指定的對象

  • ES5中 :Object.create( ) 的實現原理就源自於經典繼承

  1. 借用構造函數 實現繼承
  • 先看一下代碼 再解釋:

      // 需要兩個構造函數
      function Person(name,age,gender){  
       this.name = name;
       this.age = age;
       this.gender = gender;
      }
      // function Student(name,age,){
      //  this.name = name;
      // this.age = age;
      //  this.gender = gender;
      // }
      // 這樣的話 name,age 屬性都一樣,就會產生重復的代碼 我們可以巧妙的利用call 來簡單的實現
      function Student(name,age){
       Person.call(this,name,age);     // 其實就是用 call的方法,call借用Person的功能
       this.gender = gender;
      }
    
    • 借用Person中的構造函數的功能,this表示構造函數的實例,即Student的實例對象可以繼承name,age屬性;

    • 借用構造函數實現繼承,子構造函數借用父構造函數來完成,給子類Student的實例添加屬性

    • 注意:由於要借用父類構造函數,所以父類構造函數的功能對子類對象要適用,例如下面情況就最好不用call

      // 需要兩個構造函數
      function Person(name,age,gender){  
       this.name = name;
       this.age = age;
       this.gender = gender;
      }
      // function Student(name,age,){
      //  this.name = name;
      // this.age = age;
      // }
      function Student(name,age){
       Person.call(this,name,age);     
      }
      
  • 上述情況,就最好不要使用call來借用父類構造函數Person的功能了,因為,gender屬性是Student子類構造函數
    並不需要的,這樣的話,就會在Student中產生不必要的屬性和方法,如果子類函數還要有gender方法的話,那么就會和之前的產生沖突,交叉污染

  • 其他情況:遇到需要實現功能,該對象上沒有這個功能,可以適當地去尋找已經有功能的對象


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM