1. 程式人生 > 實用技巧 >JS 的繼承

JS 的繼承

基於原型的繼承

function DOG(name){

    this.name = name;

  }

  DOG.prototype = { species : '犬科' };


  var dogA = new DOG('大毛');

  var dogB = new DOG('二毛');


  alert(dogA.species); // 犬科

  alert(dogB.species); // 犬科

species屬性放在prototype物件裡,是兩個例項物件共享的。只要修改了prototype物件,就會同時影響到兩個例項物件。

DOG.prototype.species = '貓科';


  alert(dogA.species); // 貓科

  alert(dogB.species); // 貓科

由於所有的例項物件共享同一個prototype物件,那麼從外界看起來,prototype物件就好像是例項物件的原型,而例項物件則好像"繼承"了prototype物件一樣。
這就是Javascript繼承機制基於原型的繼承。

function Parent(name1){
  this.name1 = name1
}
Parent.prototype.pMethod = function(){
  console.log(this.name1)
}

function Child(name2, name1){
    Parent.call(this, name1) // 得分點
    this.name2 = name2
}
Child.prototype.__proto__ = Parent.prototype 
//上面這句程式碼的古板寫法應該是下面三句
//const empty = function(){}
//empty.prototype = Parent.prototype
//Child.prototype = new empty()


Child.prototype.cMethod = function(){
    console.log(this.name2)
}

基於 class 的繼承

新的關鍵字class從ES6開始正式被引入到JavaScript中。class的目的就是讓定義類更簡單。
我們先回顧用函式實現Student的方法:

function Student(name) {
    this.name = name;
}

Student.prototype.hello = function () {
    alert('Hello,' + this.name + '!');
}

如果用新的class關鍵字來編寫Student,可以這樣寫:

class Student {
    constructor(name) {
        this.name = name;
    }

    hello() {
        alert('Hello, ' + this.name + '!');
    }
}

比較一下就可以發現,class的定義包含了建構函式constructor和定義在原型物件上的函式hello()(注意沒有function關鍵字),這樣就避免了Student.prototype.hello = function () {...}這樣分散的程式碼。
最後,建立一個Student物件程式碼:

var xiaoming = new Student('小明');
xiaoming.hello()

class繼承

class定義物件的另一個巨大的好處是繼承更方便了。想一想我們從Student派生一個PrimaryStudent需要編寫的程式碼量。現在,原型繼承的中間物件,原型物件的建構函式等等都不需要考慮了,直接通過extends來實現:

class PrimaryStudent extends Student {
    constructor(name, grade) {
        super(name); // 記得用super呼叫父類的構造方法!
        this.grade = grade;
    }

    myGrade() {
        alert('I am at grade ' + this.grade);
    }
}

注意PrimaryStudent的定義也是class關鍵字實現的,而extends則表示原型鏈物件來自Student。子類的建構函式可能會與父類不太相同,例如,PrimaryStudent需要namegrade兩個引數,並且需要通過super(name)來呼叫父類的建構函式,否則父類的name屬性無法正常初始化。
PrimaryStudent已經自動獲得了父類Studenthello方法,我們又在子類中定義了新的myGrade方法。
ES6引入的class和原有的JavaScript原型繼承有什麼區別呢?實際上它們沒有任何區別,class的作用就是讓JavaScript引擎去實現原來需要我們自己編寫的原型鏈程式碼。簡而言之,用class的好處就是極大地簡化了原型鏈程式碼。

class Parent{
    constructor(name1){
        this.name1 = name1
    }
    pMethod(){
        console.log(this.name1)
    }
}
class Child extends Parent{
    constructor(name2, name1){
        super(name1) // 得分點
        this.name2 = name2
    }
    cMethod(){
        console.log(this.name2)
    }
}