一.通過(guò)構(gòu)造繼承
/* 通過(guò)構(gòu)造函數(shù)實(shí)現(xiàn)繼承 */
// 聲明父類(lèi)
function Parent (name) {
this._name = name;
this.hobby = ['讀書(shū)', '聽(tīng)歌', '打羽毛球'];
}
// 聲明父類(lèi)原型方法
Parent.prototype.getName = function () {
return this._name;
}
// 聲明子類(lèi)
function Child (name, age){
Parent.call(this, name); //這里是實(shí)現(xiàn)構(gòu)造函數(shù)繼承的精華
this._age = age;
}
// 為子類(lèi)添加原型方法
Child.prototype.getAge = function () {
return this._age;
}
隊(duì)長(zhǎng)點(diǎn)評(píng):
通過(guò)構(gòu)造函數(shù)實(shí)現(xiàn)的繼承,沒(méi)有涉及到原型prototype,所以父類(lèi)的原型方法自然不會(huì)被子類(lèi)繼承赔癌。而想要被子類(lèi)繼承就必須放在構(gòu)造函數(shù)中秒裕,這樣創(chuàng)建出來(lái)的每個(gè)實(shí)例都會(huì)單擁有一份而不能共用尔觉,這樣就違背了代碼復(fù)用的原則伐蒋。
二.通過(guò)原型繼承
/* 通過(guò)原型繼承 */
// 聲明父類(lèi)
function Parent( name ){
this._name = name;
this.hobby = ['讀書(shū)', '聽(tīng)歌', '打羽毛球'];
}
// 聲明父類(lèi)原型方法
Parent.prototype.getName = function(){
return this._name;
}
// 聲明子類(lèi)
function Child( sex ){
this._sex = sex;
}
// 繼承父類(lèi)
Child.prototype = new Parent();
// 為子類(lèi)添加原型方法
Child.prototype.getSex = function(){
return this._sex;
}
var instance1 = new Child();
var instance2 = new Child();
console.log( instance2.hobby ); //['讀書(shū)', '聽(tīng)歌', '打羽毛球']
instance1.hobby.push('跑步');
console.log( instance2.hobby ); //["讀書(shū)", "聽(tīng)歌", "打羽毛球", "跑步"]
/*
將父類(lèi)的實(shí)例賦予子類(lèi)的原型prototype,父類(lèi)創(chuàng)建的實(shí)例對(duì)象
不僅可以訪問(wèn)父類(lèi)原型上的屬性與方法,同樣也可以訪問(wèn)從父類(lèi)
構(gòu)造函數(shù)中復(fù)制的屬性和方法,將這個(gè)對(duì)象賦值給子類(lèi)的原型,則
可以訪問(wèn)父類(lèi)的原型屬性與方法的同時(shí),還可以訪問(wèn)父類(lèi)構(gòu)造函
數(shù)中復(fù)制的屬性與方法.這是原型繼承原理.
*/
隊(duì)長(zhǎng)點(diǎn)評(píng):
1.由于子類(lèi)通過(guò)其原型prototype對(duì)父類(lèi)的實(shí)例化,而繼承了父類(lèi);如果父類(lèi)中的共有屬性要是引用類(lèi)型宵膨,就會(huì)在子類(lèi)中被所有實(shí)例共用,因此一個(gè)子類(lèi)的實(shí)例更改子類(lèi)原型從父類(lèi)構(gòu)造函數(shù)中繼承來(lái)的共有屬性就會(huì)直接影響到其它子類(lèi)诸狭。
2.由于子類(lèi)實(shí)現(xiàn)的繼承是靠其原型prototype對(duì)父類(lèi)的實(shí)例化實(shí)現(xiàn)的券膀,因此在創(chuàng)建父類(lèi)的時(shí)候是無(wú)法向父類(lèi)傳遞參數(shù)的,因而在實(shí)例化父類(lèi)的時(shí)候也無(wú)法對(duì)父類(lèi)構(gòu)造函數(shù)內(nèi)的屬性進(jìn)行初始化。
三.組合繼承
/* 組合式繼承 */
// 聲明父類(lèi)
function Parent( name ){
this._name = name;
this.hobby = ['讀書(shū)', '聽(tīng)歌', '打羽毛球'];
}
// 聲明父類(lèi)原型方法
Parent.prototype.getName = function(){
return this._name;
}
// 聲明子類(lèi)
function Child( name, sex ){
//構(gòu)造函數(shù)繼承父類(lèi)name屬性
Parent.call( this, name );
// 子類(lèi)中新增的共有屬性
this._sex = sex;
}
// 通過(guò)原型繼承,子類(lèi)繼承父類(lèi)
Child.prototype = new Parent();
// 為子類(lèi)添加原型方法
Child.prototype.getSex = function(){
return this._sex;
}
var instance1 = new Child();
var instance2 = new Child();
console.log( instance2.hobby ); //['讀書(shū)', '聽(tīng)歌', '打羽毛球']
instance1.hobby.push('跑步');
console.log( instance2.hobby ); //['讀書(shū)', '聽(tīng)歌', '打羽毛球']
console.log( instance1.__proto__.constructor === Child); //false
console.log( instance1.__proto__.constructor === Parent); //true
隊(duì)長(zhǎng)點(diǎn)評(píng):
子類(lèi)的實(shí)例中更改父類(lèi)繼承下來(lái)的引用類(lèi)型屬性,不會(huì)影響到其它實(shí)例,并且子類(lèi)實(shí)例化過(guò)程中又能又能將參數(shù)傳遞到父類(lèi)的構(gòu)造函數(shù)中.
缺點(diǎn)是: 一.父類(lèi)的構(gòu)造函數(shù)調(diào)用了二遍; 二.子類(lèi)實(shí)例的原型指向的構(gòu)造函數(shù)不是指向的子類(lèi)構(gòu)造函數(shù)而是父類(lèi)的構(gòu)造函數(shù).
四.組合繼承優(yōu)化版
/* 組合式繼承優(yōu)化版 */
// 聲明父類(lèi)
function Parent( name ){
this._name = name;
this.hobby = ['讀書(shū)', '聽(tīng)歌', '打羽毛球'];
}
// 聲明父類(lèi)原型方法
Parent.prototype.getName = function(){
return this._name;
}
// 聲明子類(lèi)
function Child( name, sex ){
//構(gòu)造函數(shù)繼承父類(lèi)name屬性
Parent.call( this, name );
// 子類(lèi)中新增的共有屬性
this._sex = sex;
}
// 通過(guò)原型繼承,子類(lèi)繼承父類(lèi)
Child.prototype = Object.create( Parent.prototype );
Child.prototype.constructor = Child;
// 為子類(lèi)添加原型方法
Child.prototype.getSex = function(){
return this._sex;
}
var instance1 = new Child();
var instance2 = new Child();
console.log( instance2.hobby ); //['讀書(shū)', '聽(tīng)歌', '打羽毛球']
instance1.hobby.push('跑步');
console.log( instance2.hobby ); //['讀書(shū)', '聽(tīng)歌', '打羽毛球']
console.log( instance1.__proto__.constructor === Child); //true
console.log( instance1.__proto__.constructor === Parent); //false
隊(duì)長(zhǎng)點(diǎn)評(píng):
Object.create( Parent.prototype ); 創(chuàng)建一個(gè)中間對(duì)象實(shí)現(xiàn)連接
Child.prototype.constructor = Child; 改變子類(lèi)原型的構(gòu)造函數(shù)指向
五.多繼承
// 多繼承
var mix = function(){
var len = arguments.length, // 獲取參數(shù)長(zhǎng)度
target = arguments[0], // 第一個(gè)對(duì)象為目標(biāo)對(duì)象
arg; // 緩存參數(shù)對(duì)象
// 遍歷被繼承的對(duì)象
for( var i=1; i<len; i++ ){
// 緩存當(dāng)前對(duì)象
arg = arguments[i];
// 遍歷被繼承的對(duì)象中的屬性
for( var property in arg ){
// 將被繼承中的屬性復(fù)制到目標(biāo)對(duì)象中
target[property] = arg[property];
}
}
// 返回目標(biāo)對(duì)象
return target
}
隊(duì)長(zhǎng)點(diǎn)評(píng):
這是一個(gè)淺復(fù)制驯遇,使用時(shí)請(qǐng)注意
六.ES6的繼承
class Parent {
constructor(name){
this._name = name;
}
getName(){
return this._name;
}
}
class Child extends Parent {
constructor(name, age){
super(name);
this._age = age;
}
getAge(){
return this._age;
}
}
本文實(shí)例來(lái)源于張容銘大神寫(xiě)的《JavaScript設(shè)計(jì)模式》