天天看點

js原型繼承|26

在傳統的基于Class的語言如Java、C++中,繼承的本質是擴充一個已有的Class,并生成新的Subclass。

由于這類語言嚴格區分類和執行個體,繼承實際上是類型的擴充。但是,JavaScript由于采用原型繼承,我們無法直接擴充一個Class,因為根本不存在Class這種類型。

但是辦法還是有的。我們先回顧​

​Student​

​構造函數:

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

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

以及​

​Student​

​的原型鍊:

js原型繼承|26

現在,我們要基于​

​Student​

​​擴充出​

​PrimaryStudent​

​​,可以先定義出​

​PrimaryStudent​

​:

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

但是,調用了​

​Student​

​​構造函數不等于繼承了​

​Student​

​​,​

​PrimaryStudent​

​建立的對象的原型是:

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

必須想辦法把原型鍊修改為:

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

這樣,原型鍊對了,繼承關系就對了。新的基于​

​PrimaryStudent​

​​建立的對象不但能調用​

​PrimaryStudent.prototype​

​​定義的方法,也可以調用​

​Student.prototype​

​定義的方法。

如果你想用最簡單粗暴的方法這麼幹:

PrimaryStudent.prototype = Student.prototype;      

是不行的!如果這樣的話,​

​PrimaryStudent​

​​和​

​Student​

​​共享一個原型對象,那還要定義​

​PrimaryStudent​

​幹啥?

我們必須借助一個中間對象來實作正确的原型鍊,這個中間對象的原型要指向​

​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      

用一張圖來表示新的原型鍊:

js原型繼承|26

注意,函數​

​F​

​​僅用于橋接,我們僅建立了一個​

​new F()​

​​執行個體,而且,沒有改變原有的​

​Student​

​定義的原型鍊。

如果把繼承這個動作用一個​

​inherits()​

​​函數封裝起來,還可以隐藏​

​F​

​的定義,并簡化代碼:

function inherits(Child, Parent)
    var F = function ()};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
}      
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;      

繼續閱讀