《javascript高級程序設計》 繼承實現方式

這篇原本應該是做爲寫JS 面向對象的前奏,只是做爲《javascript高級程序設計》繼承一章的筆記javascript

  • 原型鏈java

    • code 實現shell

      function SuperType() {
        this.colors = ['red','blue', 'green'];
      }
      function SubType() {
      }
      SubType.prototype = new SuperType();
      var instance1 = new SubType();
      instance1.colors.push("black");
      console.log(instance1.colors); // ['red','blue', 'green','black']
      
      var instance2 = new SubType();
      console.log(instance2.colors); // ['red','blue', 'green','black']
      
      var instance = new SuperType();
      console.log(instance.colors); // ['red','blue', 'green']
    • 使用原型鏈來實現繼承,原型實際上會變成另外一個類型的實例,因而,原先的實例屬性,會變成如今的原型屬性了
    • 在建立子類的實例時,不能向父類的構造函數中傳遞參數
  • 借用構造函數segmentfault

    • code 實現繼承函數

      function SuperType() {
        this.colors = ["red","blue","green"];
      }
      function SubType() {
        SuperType.call(this);
      }
      
      var instance1 = new SubType();
      instance1.colors.push("black");
      console.log(instance1.colors); // ["red", "blue", "green", "black"]
      
      var instance2 = new SubType();
      console.log(instance2.colors); // ["red", "blue", "green"]
      
      var instance = new SuperType();
      console.log(instance.colors); // ['red','blue', 'green']

    一樣也能夠實現參數的傳遞this

    function SuperType(name) {
      this.name = name;
    }
    function SubType(){
      SuperType.call(this, "jack");
      this.age = 29;
    }
    
    var instance = new SubType();
    console.log(instance.name); // jack
    console.log(instance.age); // 29
    • 若是僅僅是借用構造函數,那麼將沒法避免構造函數模式存在的問題--方法都在構造函數中定義,所以,函數複用也就無從談起了。並且,在超類型的原型中定義的方法,對子類而言也是不可見的,結果全部類型都只能使用構造函數模式。
  • 組合繼承prototype

    • 將原型鏈和借用構造函數的技術組合到一塊,從而發回兩者之長的一種繼承模式。其背後的思路是使用原型鏈實現對原型屬性和方法的繼承,而經過借用構造函數來實現對實例屬性的繼承。這樣,即經過在原型上定義方法實現了函數複用,又可以保證每一個實例都有它本身的屬性。
    • code 實現設計

      function SuperType(name) {
        this.name = name;
        this.colors = ['red','blue','green'];
      }
      SuperType.prototype.sayName = function() {
        console.log(this.name);
      };
      function SubType(name, age) {
        SuperType.call(this, name);
        this.age = age;
      };
      
      SubType.prototype = new SuperType();
      SubType.prototype.sayAge = function(){
        console.log(this.age);
      };
      
      var instance1 = new SubType("jack", 29);
      instance1.colors.push("black");
      console.log(instance1.colors); //["red", "blue", "green", "black"]
      instance1.sayName(); // jack
      instance1.sayAge(); // 29
      
      var instance2 = new SubType("allen", 23);
      console.log(instance2.colors); // ["red", "blue", "green"]
      instance2.sayName(); //allen
      instance2.sayAge(); // 23
    • instanceOfisPrototypeOf 也可以用於識別基於組合繼承建立的對象
  • 原型式繼承code

    • 沒有嚴格意義上的構造函數,經過藉助原型,能夠基於已有的對象建立新對象,同時還沒必要所以建立自定義類型對象

      function object(o){
        function F(){};
        F.prototype = o;
        return new F();
      }

    object() 函數內部,先建立了一個臨時性的構造函數,而後將傳入的對象做爲這個構造函數的原型,最後返回這個臨時類型的一個新實例。從本質上將,object() 對傳入其中的對象執行了一次淺複製

    function object(o) {
        function F() {};
        F.prototype = o;
        return new F();
    }
    
    var person = {
        name:'jack',
        friends:['allen','lucy','van']
    }
    
    var anotherPerson = object(person);
    anotherPerson.name = 'bob';
    anotherPerson.friends.push('steve');
    
    var yetAnotherPerson = object(person);
    yetAnotherPerson.name = 'linda';
    yetAnotherPerson.friends.push('shelly')
    
    console.log(person.friends); //["allen", "lucy", "van", "steve", "shelly"]

    這種原型式繼承,要求你必須有一個對象能夠做爲另外一個對象的基礎。若是有這麼一個對象的話,能夠把他傳遞給object() 函數,而後再根據具體需求對獲得的對象加以修飾便可。

    ECMAScript5 經過Object.create() 方法規範花了原型式繼承。這個方法接受兩個參數:一個用做新對象原型的對象和(可選的)一個爲新對象定義額外屬性的對象。在傳入一個參數的狀況下,Object,create()object() 函數方法的行爲相同

    在沒有必要建立構造函數,而只是想讓一個對象與另外一個對象保持相似的狀況下,原型式繼承是徹底能夠勝任的。不過,包含引用類型值的屬性始終都會共享響應的值,就像使用原型模式同樣

  • 寄生式繼承

    • 寄生式繼承是與原型式繼承緊密相關的一種思路。寄生式繼承的思路與寄生構造函數和工廠模式相似,即建立一個僅用於封裝繼承過程的函數,該函數在內部已某種方式來加強對象,最後再像真的是它作了全部工做同樣返回對象。

      function createAnother(original){
        var clone = object(original); // 經過調用 object() 函數建立一個新對象
        clone.sayHi = function(){  // 以某種方式來加強對象
          alert("hi");
        };
        return clone; // 返回這個對象
      }
    • 在主要考慮對象而不是自定義類型和構造函數的狀況下,寄生式繼承也是一種有用的模式。前面示範繼承模式時使用的object() 函數不是必需的;任何可以返回新對象的函數都使用與此模式。
  • 寄生組合式繼承

    • 組合繼承是JavaScript 最經常使用的繼承模式;不過也有本身的不足。組合繼承最大的問題就是不管什麼狀況下,都會調用兩次父類的構造函數:一次是在建立子類原型的時候,另外一次是在子類構造函數內部。

    子類最終會包含父類對象的所有實例屬性,但咱們不得不在調用子類構造函數時重寫這些屬性。

    function SuperType(name) {
        this.name = name;
        this.colors = ['red','blue','green'];
    }
    
    SuperType.prototype.sayName = function(){
        console.log(this.name);
    }
    
    function SubType(name, age) {
        SuperType.call(this, name); // 第二次調用 SuperType()
        this.age = age;
    }
    
    SubType.prototype = new SuperType(); // 第一次調用 SuperType()
    SubType.prototype.constructor = SubType;
    SubType.prototype.sayAge = function() {
        console.log(this.age);
    };

    第一次調用SuperType 構造函數時,SubType.prototype會獲得兩個屬性:namecolors,他們都是SuperType 的實例屬性,只不過如今位於SubType 的原型中。當調用SubType 構造函數時,又會調用一次SuperType 構造函數,這一次又在新對象上建立了實例屬性namecolors。 因而,這兩個屬性就屏蔽了原型中的兩個同名屬性。

    所謂寄生組合式繼承,即經過借用構造函數來繼承屬性,經過原型鏈的混成形式來繼承方法。其背後的基本思路是:沒必要爲了指定子類的原型而調用父類的構造函數,咱們所須要的無非就是父類原型的一個副本而已。本質上,就是使用寄生式繼承來繼承父類型的原型,而後再將結果指定給子類的原型。寄生組合式繼承的基本模式以下

    function inheritPrototype(subType, superType) {
      var prototype = object(superType.prototype);
      prototype.constructor = subType;
      subType.prototype = prototype;
    }

    第一步是建立父類原型的一個副本,第二步是爲建立的副本添加constructor 屬性,從而彌補因重寫原型而失去的默認的constructor 屬性。第三步是將新建立的對象(即副本)賦值給子類的原型。

    function SuperType(name) {
        this.name = name;
        this.colors = ['red','blue','green'];
    }
    
    SuperType.prototype.sayName = function(){
        console.log(this.name);
    }
    
    function SubType(name, age) {
        SuperType.call(this, name); // 第二次調用 SuperType()
        this.age = age;
    }
    
    inheritPrototype(SubType, SuperType)
    SubType.prototype.sayAge = function() {
        console.log(this.age);
    };

    這個例子的高效率體如今它只調用了一次SuperType 構造函數,而且所以避免了在SubType.prototype 上建立沒必要要的、多餘的屬性。於此同時,原型鏈還能保持不變;所以,還可以正常使用instanceofisPrototypeOf() 。開發人員廣泛認爲寄生組合繼承是引用類型最理想的繼承範式。

優缺點

  • 原型鏈會修改父類的屬性,在建立子類的實例時,不能向父類的構造函數中傳遞參數
  • 借用構造函數,則沒有繼承
  • 組合繼承(原型繼承+借用構造函數) 組合繼承最大的問題就是不管什麼狀況下,都會調用兩次父類的構造函數:一次是在建立子類原型的時候,另外一次是在子類構造函數內部。
  • 原型式繼承,要求你必須有一個對象能夠做爲另外一個對象的基礎,
    包含引用類型值的屬性始終都會共享響應的值,就像使用原型模式同樣
  • 寄生式繼承
    在主要考慮對象而不是自定義類型和構造函數的狀況下,寄生式繼承也是一種有用的模式
  • 寄生組合式繼承 (這是最成熟的方法,也是如今庫實現的方法)
    第一步是建立父類原型的一個副本,第二步是爲建立的副本添加constructor 屬性,從而彌補因重寫原型而失去的默認的constructor 屬性。第三步是將新建立的對象(即副本)賦值給子類的原型。


JS面向對象系列

相關文章
相關標籤/搜索