1. 程式人生 > >面向物件程式設計--繼承

面向物件程式設計--繼承

一.子類的原型繼承 -- 類式繼承

  // 宣告父類

  function SuperClass(){

    this.superValue = true

  }

  //為父類新增共有方法

  SuperClass.prototype.getSuperValue = function(){

    return this.superValue

  }

  //宣告子類

  function SubClass(){

    this.subValue = false;

  }

  //繼承父類

  subClass.prototype = new SuperClass();

  //為子類新增公共的方法

  SubClass.prototype.getSubValue = function(){

    return this.subValue

  }

  //

  var b = new SubClass();
  console.log(b.getSuperValue())   // true

總結:類式繼承需要將父類的例項賦值給子類的原型。why?類的原型物件的作用就是為類的原型新增共有方法,但類不能直接訪問這些屬性和方法,必須通過原型prototype來訪問。例項化一個父類,新建立的物件複製了父類的建構函式內的屬性與方法並且將原型__proto__指向了父類的原型物件。這樣就擁有了父類的原型物件上的屬性與方法,並且這個新建立的物件課直接訪問到父類原型物件上的屬性與方法。如果我們將這個新建立的物件賦值給子類的原型,那麼子類的原型就可以訪問大父類的原型屬性與方法了。新建的物件不僅僅可以訪問父類原型上的屬性和方法,也可以訪問從父類建構函式中複製出來的屬性與方法。 將這個賦值給子類的原型,那麼這個子類的原型同樣可以訪問父類原型上的屬性與方法與從父類建構函式中複製出來的屬性與方法。  ---類式繼承原理

 

通過instanceof檢測某個物件是否是某個類的例項,或者說某個物件是否繼承了某個類。這樣就可以判斷物件與類之間的關係了

instanceof是通過判斷物件的prototype鏈來確定這個物件是否是某個物件的例項,而不關心物件與類的自身結構。

 

console.log(b instanceof SuperClass);   //true

console.log(b instanceof SubClass);   //true

console.log(SubClass instanceof SuperClass);  //false

console.log(SubClass.prototype instanceof SuperClass);  //true

//你所建立的物件都是Object的例項,Javascript為我們提供的原生物件Object.

 

console.log(SuperClass instanceof Object)  //true

console.log(SubClass instanceof Object)  //true

console.log(b instanceof Object)  //true

 

Object是所有物件的祖先。

 

缺點:

  1.由於子類通過器原型prototype對父類例項化,繼承了父類,所以說父類中的共有屬性必須是引用型別,就會在子類中被所有例項公用,因此一個子類的例項改變子類原型從父類建構函式中繼承來的共有屬性就會直接影響待其他子類。     

    var SuperClass =function(){
      this.book = ['html',"css","javascript"];
      this.superValue = true
    }
    //為父類新增共有方法
    SuperClass.prototype.getSuperValue = function(){
      return this.superValue
    }
    //宣告子類
    var SubClass = function(){
      this.subValue = false;
    }
    //繼承父類
    SubClass.prototype = new SuperClass();
    //為子類新增公共的方法
    SubClass.prototype.getSubValue = function(){
      return this.subValue;
    }

    var b = new SubClass();
    var b1 = new SubClass()
    console.log(b.book) //["html", "css", "javascript"]
    console.log(b1.book) //["html", "css", "javascript"]
    b1.book.push("python");
    console.log(b.book) //["html", "css", "javascript", "python"]
    console.log(b1.book) //["html", "css", "javascript", "python"]

 

  2.由於子類實現的繼承是靠其原型prototype對父類的例項化的,因此,在建立父類的時候,無法向父類傳遞引數,因此在例項化父類的時候也無法對父類的建構函式內的屬性進行初始化。

 

 

二:建立即繼承 -- 建構函式繼承

  //建構函式式繼承

  //宣告父類

  function SuperClass(id){

    //引用型別共有屬性

    this.books = ['javascript','html','css'];

    //值型別共有屬性

    this.id = id;

  }

  //宣告父類原型方法

  SuperClass.prototype.showBooks = function(){

    console.log(this.books)

  }

 

  //宣告子類

  function SubClass(id){

    console.log(this)    //SubClass {}

    //呼叫一個物件的一個方法,用另一個物件替換當前物件。例如:B.call(A, args1,args2);即A物件呼叫B物件的方法。

    SuperClass.call(this,id);

  }

  //建立第一個子類的例項

  var instance1 = new SubClass(10)

  //建立第一個子類的例項

  var instance2 = new SubClass(10)

  

  //每個例項都會單獨擁有一份而不能供用

  console.log(instance1)
    // SubClass {books: Array(3), id: 10}
    // books: (4) ["javascript", "html", "css", "設計模式"]
    // id: 10
    // __proto__: Object
  console.log(instance2)
    // SubClass {books: Array(3), id: 11}
    // books: (3) ["javascript", "html", "css"]
    // id: 11
    // __proto__: Object

  instance1.books.push("設計模式");
  console.log(instance1.books)      //["javascript", "html", "css", "設計模式"]
  console.log(instance1.id)      //10
  console.log(instance2.books)              //["javascript", "html", "css"]
  console.log(instance2.id)      //11

 

  特點: 由於Call這個方法可以更改函式的作用環境,因此在子類中,對SuperCalss呼叫這個方法就是將子類中的變數在父類在執行一遍,由於父類中是給this繫結屬性的,因此子類資源就繼承了父類的共有屬性。

  缺點:由於這種型別的繼承沒有設計原型prototype,所以父類的原型自然不會被子類繼承,而如果想要被子類繼承就必須放在建構函式中,這樣創建出來的每個例項都會單獨擁有一份而不能供用,違背了程式碼複用的原則。

 

三:組合繼承

  類式繼承是要通過子類的原型prototype對父類的例項化來的,

  建構函式式繼承是通過子類的建構函式作用環境中執行一次父類的建構函式來實現的。

  

  //宣告父類
  function SuperClass(name){
    //值型別共有屬性
    this.name = name;
    //引用型別共有屬性
    this.books = ["html","css","javascript"];
  }
  //父類原型屬性共有方法
  SuperClass.prototype.getName = function(){
    console.log(this.name)
  }
  //宣告子類
  function SubClass(name,time){
    //構造韓式式繼承父類name屬性
    SuperClass.call(this, name);
    //子類中新增共有屬性
    this.time = time;
  }
  // 類式繼承 子類原型繼承父類
  SubClass.prototype = new SuperClass();
  // 子類原型方法
  SubClass.prototype.getTime = function(){
    console.log(this.time)
  }

  var instance1 = new SubClass("js book", 2014);
  console.log(instance1)
    // SubClass {name: "js book", books: Array(3), time: 2014}
    // books: (4) ["html", "css", "javascript", "設計模式"]
    // name: "js book"
    // time: 2014
    // __proto__: SuperClass

  instance1.books.push("設計模式");
  console.log(instance1.books) //["html", "css", "javascript", "設計模式"]
  instance1.getTime() //2014
  instance1.getName() //js book

  var instance2 = new SubClass("css book", 2012);
  console.log(instance2)
    //SubClass {name: "css book", books: Array(3), time: 2012}
    //books: (3) ["html", "css", "javascript"]
    //name: "css book"
    //time: 2012
    //__proto__: SuperClass
  console.log(instance2.books) //["html", "css", "javascript"]
  instance2.getTime() //2012
  instance2.getName() //css book

 

總結: 子類的例項中更改父類繼承下來的引用型別屬性,根本不會影響到其他例項,並且子類例項化過程中又能將引數傳遞到父類的建構函式。

缺點: 使用建構函式繼承時執行了一遍父類的建構函式,而在實現子類原型的類式繼承又呼叫了一遍父類的建構函式,因此父類呼叫了兩遍。