目錄
- 類式繼承
- 建構式繼承
- 組合繼承
- 寄生組合式繼承
- 基礎了解
- 原型式繼承
- 寄生式繼承
- 實作
類式繼承
//宣告父類
//宣告父類
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 instance = new SubClass();
console.log(instance.getSuperValue()); //true
console.log(instance.getSubValue()); //false
類式繼承需要將父類的實體賦值給子類原型, subClass.prototype繼承了superClass,
這種類式繼承有兩個缺點,其一,由于子類通過原型prototype對父類實體化,繼承了父類,父類中的共有屬性要是參考型別,就會在子類中被所有實體共用,因此一個子類的實體更改子類原型從父類建構式中繼承來的共有屬性就會直接影響到其他子類,如下:
function SuperClass() {
this.courses = ['語文', '數學', '英語']
}
function SubClass() {}
SubClass.prototype = new SuperClass();
var instance1 = new SubClass()
var instance2 = new SubClass()
console.log(instance2.courses) //['語文', '數學', '英語']
instance1.courses.push('化學')
console.log(instance2.courses) //['語文', '數學', '英語', '化學']
instance1的修改直接影響了instance2,這是一個災難的陷阱,其二,由于子類實作的繼承是靠其原型prototype對父類的實體化實作的,因此在創建父類的時候,是無法向父類傳遞引數的,因而在實體化父類的時候也無法對父類建構式內的屬性進行初始化,如何解決這個問題?請繼續往下看,
建構式繼承
function SuperClass(current) {
this.courses = ["語文", "數學", "英語"];
this.current = current;
}
//父類宣告原型方法
SuperClass.prototype.getCourses= function () {
console.log(this.courses);
};
//宣告子類
function SubClass(current) {
SuperClass.call(this, current);
}
var instance1 = new SubClass("語文");
var instance2 = new SubClass("數學");
instance1.courses.push('化學')
console.log(instance1.courses); //["語文", "數學", "英語", "化學"]
console.log(instance1.current); //語文
console.log(instance2.courses); //["語文", "數學", "英語"]
console.log(instance2.current); //數學
instance1.getCourses() //TypeError: instance1.getCourses is not a function
SuperClass.call(this, current) 這條陳述句是建構式繼承的精華,由于call這個方法可以更改函式的作用環境,因此在子類中,對SuperClass呼叫這個call就是將子類中變數在父類中執行一遍,由于父類中是給this系結屬性的,因此子類也就繼承了父類的共有屬性,
由于這種型別的繼承沒有涉及原型prototype,所以父類的的原型方法不會被子類繼承,要想被子類繼承,只能將showCourse 放在父類建構式中,但是這樣就違背了代碼復用的原則,為了綜合以上兩種繼承的優點,于是有了組合繼承,
組合繼承
//組合繼承
function SuperClass(current) {
//參考型別共有屬性
this.courses = ["語文", "數學", "英語"];
// 值型別共有屬性
this.current = current;
}
SuperClass.prototype.getCourses = function () {
console.log(this.courses);
};
SuperClass.prototype.getCurrent = function () {
console.log(this.current);
};
// 宣告子類
function SubClass(current, time) {
//建構式繼承父類屬性
SuperClass.call(this, current);
this.time = time;
}
//類式繼承 子類原型繼承父類
SubClass.prototype = new SuperClass();
//子類原型方法
SubClass.prototype.getTime = function () {
console.log(this.time);
};
在子類建構式中執行父類建構式,在子類的原型上實體化父類就是組合模式,子類實體中更改父類繼承下來的參考型別屬性courses 不會改變其他實體,測驗如下
var instance1 = new SubClass("語文", "9:00");
instance1.getTime(); //9:00
instance1.courses.push('化學')
instance1.getCourses(); //["語文", "數學", "英語", "化學"]
instance1.getCurrent(); //語文
console.log(instance1.current)//語文
var instance2 = new SubClass("數學", "10:00");
instance2.getTime(); //10:00
instance2.getCourses(); //["語文", "數學", "英語"]
instance2.getCurrent(); //數學
console.log(instance2.current)//數學
但是該模式在執行子類建構式時執行了一遍父類函式,在實作子類原型繼承時又執行了一遍父類建構式,呼叫了父類建構式兩遍,顯然是一個設計缺陷,那還有更好的方式嗎?針對該缺陷,出現了“寄生組合式繼承”
寄生組合式繼承
在介紹這種繼承方式之前,需要先了解 “原型式繼承”和“寄生式繼承”
基礎了解
原型式繼承
function inheritObject(o) {
function F() {}
F.prototype = o;
return new F();
}
var course = {
name: "語文",
alikeCourse: ["數學", "英語"],
};
var newCourse = inheritObject(course);
newCourse.name = "化學";
newCourse.alikeCourse.push("物理");
var otherCourse = inheritObject(course);
otherCourse.name = "政治";
otherCourse.alikeCourse.push("歷史");
console.log(newCourse.name); //化學
console.log(newCourse.alikeCourse); //["數學", "英語", "物理", "歷史"]
console.log(otherCourse.name); //政治
console.log(otherCourse.alikeCourse); //["數學", "英語", "物理", "歷史"]
console.log(course.name); //語文
console.log(course.alikeCourse); //["數學", "英語", "物理", "歷史"]
inheritObject可以看做是對類式繼承的一種封裝,其中的過度類F相當于類式繼承中的子類,在類式繼承中存在的共用參考型別的問題依然存在,但是過度類F建構式中無內容,所以開銷較小,
寄生式繼承
“寄生式繼承”是在“原型式繼承”的基礎上繼續增強,
function inheritObject(o) {
function F() {}
F.prototype = o;
return new F();
}
var course = {
name: "語文",
alikeCourse: ["數學", "英語"],
};
function createCourse(obj) {
//通過原型繼承方式創建新物件
var o = new inheritObject(obj);
// 拓展新物件
o.getName = function () {
console.log(this.name);
};
return o;
}
const newCourse = createCourse(course)
這種方式在某個物件內部持續增長屬性,像寄生式生長,所以稱之為寄生繼承,寄生繼承是對原型繼承的二次封裝,并且在二次封裝的程序中對繼承的物件做了拓展,這樣新創建的物件不僅僅有父類中的屬性和方法,而且還添加了新的屬性和方法,在這種思想的基礎上,結合組合式繼承,衍生了 “寄生組合式繼承”
實作
function inheritObject(o) {
function F() {}
F.prototype = o;
return new F();
}
function inheritPrototype(subClass, superClass) {
//復制一份父類的原型副本保存在變數中
var p = inheritObject(superClass.prototype)
//修正因為重寫子類原型導致子類的constructor屬性被修改
p.constructor = subClass
//設定子類的原型
subClass.prototype = p
}
以上父類原型保存一個副本,賦值給子類原型,從而實作繼承,且并未重新呼叫一次父類函式,測驗如下,與組合繼承模式相近
//test
function SuperClass(current) {
//參考型別共有屬性
this.courses = ["語文", "數學", "英語"];
// 值型別共有屬性
this.current = current;
}
SuperClass.prototype.getCourses = function () {
console.log(this.courses);
};
SuperClass.prototype.getCurrent = function () {
console.log(this.current);
};
// 宣告子類
function SubClass(current, time) {
//建構式繼承父類屬性
SuperClass.call(this, current);
this.time = time;
}
//寄生式繼承 子類原型繼承父類
inheritPrototype(SubClass, SuperClass);
//類式繼承 子類原型繼承父類
// SubClass.prototype = new SuperClass();
//子類原型方法
SubClass.prototype.getTime = function () {
console.log(this.time);
};
var instance1 = new SubClass("語文", "9:00");
var instance2 = new SubClass("數學", "10:00");
instance1.getTime(); //9:00
instance1.courses.push("化學");
instance1.getCourses(); //["語文", "數學", "英語", "化學"]
instance1.getCurrent(); //語文
console.log(instance1.current); //語文
instance2.getTime(); //10:00
instance2.getCourses(); //["語文", "數學", "英語"]
instance2.getCurrent(); //數學
console.log(instance2.current); //數學
區別僅在
//寄生式繼承 子類原型繼承父類
inheritPrototype(SubClass, SuperClass);
//類式繼承 子類原型繼承父類
// SubClass.prototype = new SuperClass();
從而實作多個子類多個實體不相互影響,父類建構式僅僅呼叫一次,堪當JavaScript繼承的終極模式,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/254959.html
標籤:其他
上一篇:如何撰寫一個d.ts檔案
下一篇:C語言遞回實作n階漢諾塔操作程序
