javascript原型繼承

在傳統的基於Class的語言如Java、C++中,繼承的本質是擴展一個已有的Class,並生成新的Subclass。函數

因爲這類語言嚴格區分類和實例,繼承其實是類型的擴展。可是,JavaScript因爲採用原型繼承,咱們沒法直接擴展一個Class,由於根本不存在Class這種類型。this

可是辦法仍是有的。咱們先回顧Student構造函數:spa

function Student(props) {
    this.name = props.name || 'Unnamed';
}

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

如今,咱們要基於Student擴展出PrimaryStudent,能夠先定義出PrimaryStudentprototype

function PrimaryStudent(props) {
    // 調用Student構造函數,綁定this變量:
    Student.call(this, props);
    this.grade = props.grade || 1;
}

可是,調用了Student構造函數不等於繼承了StudentPrimaryStudent建立的對象的原型是:code

new PrimaryStudent() ----> PrimaryStudent.prototype ----> Object.prototype ----> null

必須想辦法把原型鏈修改成:對象

new PrimaryStudent() ----> PrimaryStudent.prototype ----> Student.prototype ----> Object.prototype ----> null

這樣,原型鏈對了,繼承關係就對了。新的基於PrimaryStudent建立的對象不但能調用PrimaryStudent.prototype定義的方法,也能夠調用Student.prototype定義的方法。blog

若是你想用最簡單粗暴的方法這麼幹繼承

PrimaryStudent.prototype = Student.prototype;

是不行的!若是這樣的話,PrimaryStudentStudent共享一個原型對象,那還要定義PrimaryStudent幹啥?(就像,父親生個兒子,是讓保持他身上的特性,能夠幫父親幹活,可是生出的兒子去幹指定的活,是讓父親幫他乾的,那生這個兒子幹啥。。。)ip

咱們必須藉助一箇中間對象來實現正確的原型鏈,這個中間對象的原型要指向Student.prototype。爲了實現這一點,參考道爺(就是發明JSON的那個道格拉斯)的代碼,中間對象能夠用一個空函數F來實現:原型鏈

// PrimaryStudent構造函數:
function PrimaryStudent(props) {
    Student.call(this, props);
    this.grade = props.grade || 1;
}

// 空函數F:
function F() {
}

// 把F的原型指向Student.prototype:
F.prototype = Student.prototype;

// 把PrimaryStudent的原型指向一個新的F對象,F對象的原型正好指向Student.prototype:
PrimaryStudent.prototype = new F();

// 把PrimaryStudent原型的構造函數修復爲PrimaryStudent:
PrimaryStudent.prototype.constructor = PrimaryStudent;

// 繼續在PrimaryStudent原型(就是new F()對象)上定義方法:
PrimaryStudent.prototype.getGrade = function () {
    return this.grade;
};

// 建立xiaoming:
var xiaoming = new PrimaryStudent({
    name: '小明',
    grade: 2
});
xiaoming.name; // '小明'
xiaoming.grade; // 2

// 驗證原型:
xiaoming.__proto__ === PrimaryStudent.prototype; // true
xiaoming.__proto__.__proto__ === Student.prototype; // true

// 驗證繼承關係:
xiaoming instanceof PrimaryStudent; // true
xiaoming instanceof Student; // true

發現這樣繼承的步驟有點繁瑣了,能夠將它封裝起來!!!

若是把繼承這個動做用一個inherits()函數封裝起來,還能夠隱藏F的定義,並簡化代碼:

function inherits(Child, Parent) {
    var F = function () {};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
}

這個inherits()函數能夠複用:

function Student(props) {
    this.name = props.name || 'Unnamed';
}

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

function PrimaryStudent(props) {
    Student.call(this, props);
    this.grade = props.grade || 1;
}

// 實現原型繼承鏈:
inherits(PrimaryStudent, Student);

// 綁定其餘方法到PrimaryStudent原型:
PrimaryStudent.prototype.getGrade = function () {
    return this.grade;
};

小結

JavaScript的原型繼承實現方式就是:

  1. 定義新的構造函數,並在內部用call()調用但願「繼承」的構造函數,並綁定this

  2. 藉助中間函數F實現原型鏈繼承,最好經過封裝的inherits函數完成;

  3. 繼續在新的構造函數的原型上定義新方法。

相關文章
相關標籤/搜索