javascript繼承之學習筆記

今天記錄一下學習javascript的繼承。javascript

繼承基本上是基於「類」來講的,而javascript中並不存在真正的類,因此就出現了各類模擬「類」的行爲,而後就冠冕堂皇的使用起了類的概念。這裏不談「類」的概念對js學習形成了多大的困難,只說一下普通意義上繼承的6種方式。java

一、原型鏈繼承app

這是js開發人員一開始接觸js,最容易學會的,因此說他就是最簡單的繼承的方法:函數

function Super(val) {
   this.val = val;
   this.arr = [1];
 }
 Super.prototype.fn = function () {
   console.log(this.arr)
 }
 
 function Subtype(val, name) {}
 Subtype.prototype = new Super();
 var b1 = new Subtype(12);
 console.log(b1.val)				// undefined
 var b2 = new Subtype();
 b1.fn();        			// [1]   
 b1.arr.push(2);     
console.log(b2.arr);        //  [1,2]

很容易的構造了一個子類Subtype,繼承了父類的全部屬性和共享方法。學習

  可是缺點也很明顯:this

    一、子類沒法傳入初始參數,對於這裏而言,就是Supertype沒法爲內部屬性val傳遞一個合理的值;b1實例化的時候,爲val傳入參數12,可是b1.val卻取不到該值。prototype

    二、若是屬性值不是一個標量基本類型,是一個對象,則會出現若是子類實例對該對象進行修改,因爲對象屬性所存儲的僅僅是一個引用,而不是其真實值,因此會致使修改一個實例的對象屬性的值,致使另外一個實例的該對象引用的值跟着變化。這裏就是對實例b1的屬性arr添加值操做的話,實例2引用的arr會同事變化,由於本質上他們所指向的是一個值。對象

爲了修改上述問題,則引伸出了第二種繼承方式:blog

二、借用構造函數繼承

     function Super(val) {
        this.val = val;
        this.arr = [1];
        this.fn = function () {
          console.log('prototype');
        }
      }
      
      function Subtype() {
        Super.apply(this, arguments);
      }
      var sub1 = new Subtype('s', 'zhongg');
      var sub2 = new Subtype('sdsdds', 'fsdajfsldaf');
      sub1.arr.push(5);
      console.log(sub2.arr);

  很明顯,修改了上述原型鏈繼承方式的兩個存在問題,可是卻又產生了一個新的問題,函數不能公用的問題,因爲子類是直接調用的父類函數,這就與原型prototype沒有任何關係了,從而會致使定義在原型prototype上的任何函數都不起做用,這麼一來,全部須要用到的函數就必須定義在父類函數內部。當子類比較多,實例化不少的時候,這就會形成每個實例化都實例化了相同的函數,會佔用大量內存。

  那麼必然的,既然目前的解決方案還不完美,確定會有更完美的解決方案:

三、組合繼承

      function Super(val) {
        this.val = val;
        this.arr = [1];
      }
      Super.prototype.fn = function () {
        console.log(this.val + 'prototype');
      }
      function Subtype(val, name) {
        Super.apply(this, arguments);
      }
      Subtype.prototype = new Super();
      var sub1 = new Subtype('值', '鍵值對');
      var sub2 = new Subtype('值2', '鍵值對2');
      sub1.fn();

  組合繼承,顧名思義,就是組合了其餘的繼承方式而成,這裏其實就是組合了原型鏈繼承和借用構造函數繼承。

  已經基本上完美了,綜合了前兩種繼承方式的全部優勢。既然說是基本上完美,那確定仍是有點瑕疵的,這裏的瑕疵就是調用了兩次父類函數,一次直接調用,一次new調用,致使生成了兩份的實例屬性,對於內存而言也是一種浪費。

  到ES5時候,出現了一種能夠真正完美解決繼承全部缺點的繼承方式:

四、寄生組合繼承  

     function Super(val) {
        this.val = val;
        this.arr = [1];
      }
      Super.prototype.fn = function () {
        console.log(10 + this.val);
      }
      function Subtype(val, name) {
        Super.apply(this, arguments);
        this.name = name;
      }
      Subtype.prototype = Object.create(Super.prototype);
      Subtype.prototype.fun = function () {
        console.log("fnu:" + this.name);
      } 
      Subtype.prototype.constructor = Subtype;
      var sub1 = new Subtype('zhi', '名字是什麼比較好呢?');
      var sub2 = new Subtype('again', 'nameto');
      console.log(sub1.constructor)
      sub1.fun();
      sub1.fn();
      sub1.arr.push(3);    

  利用的是ES5當中的Object.create(...),《你不知道的javascript》中對Object.create(..)的解釋:Object.create(..)會建立一個新對象並把他關聯到咱們指定的對象。這裏就是建立一個新的對象 Subtype.prototype 並把他關聯到 Super.prototype,這裏也會產生另一個小問題,須要進行修補。就是Object.create(..)建立的對象沒有constructor屬性,若是須要constructor屬性的話,那麼在Subtype.prototype建立以後,須要手動修補,Subtype.prototype.constructor = Subtype。這麼一來,繼承上產生的各類問題都真正完美了。

  因爲Object.create(...)是在ES5中定義的,因此這個方案提出以前,其實利用的是Object.create(...)的polyfill的方案,相似於:  

    if (!Object.create) {
        Object.create = function (o) {
          function F (){};
          F.prototype = o;
          return new F();
        }
      }

   可是因爲該方案出現的時間比較晚,雖然此方案是真正完美的方案,可是並無上述的組合繼承的方案使用普遍。事實上,組合繼承的方案也基本上可以保證咱們的平常開發了。

另外還有兩種繼承方案,但是已經不太像繼承了:

五、原型式繼承

      function Super(val) {
        this.val = val;
        this.arr = [1];
      }
      Super.prototype.fn = function () {
        console.log(10 + this.val);
      }
      var sup = new Super('父類');
      var sub = Object.create(sup);
      sub.func1= function(){}; 
    // 後面就是爲子類sub添加各類須要的方法或者屬性

  仔細比較一下,其實原型式繼承和原型鏈繼承仍是比較類似的,區別在於原型鏈繼承的 Subtype.prototype = new Super();而原型式繼承爲 var sup = new Super('父類'); var sub = Object.create(sup);應該可以看出區別所在了。

六、寄生繼承

最後一種繼承方式真心的不是很想說,沒什麼可說的,其實就是把原型式繼承的子類繼承部分封裝成函數來實現而已。

     function Super(val) {
        this.val = val;
        this.arr = [1];
      }
      Super.prototype.fn = function () {
        console.log(10 + this.val);
      }
      function Subtype(o){
        var clone = Object.create(o);
        clone.func1= function(){};
        //  添加各類屬性
      }
      var sub = Subtype(new Super());    

  在平常開發中,通常使用第三種組合繼承的方式,若是想要求更高一點的話,可使用第四種寄生組合繼承的方式。

相關文章
相關標籤/搜索