首頁 > 軟體

JS繼承與工廠構造及原型設計模式詳解

2022-07-08 14:06:36

序言

我們在前一篇文章《JS精粹,原型鏈繼承和建構函式繼承的 “毛病”》 ,提到了:原型鏈繼承、建構函式繼承、組合繼承;

在另一篇文章《驀然回首,“工廠、構造、原型”設計模式,正在燈火闌珊處》,提到了:我們用於建立物件的三種設計模式:工廠設計模式、構造設計模式、原型設計模式;

至此,我們可以明顯的感受到:JS 要實現物件導向(繼承的能力),離不開這 3 種設計模式;

原型鏈 + 建構函式 = 組合繼承

本篇帶來一個新的繼承方式:寄生繼承,它由工廠模式和建構函式模式組成,即

工廠+建構函式 = 寄生繼承

正文

正是由於:原型鏈繼承和建構函式繼承的 “毛病”

  • 原型鏈繼承:所有繼承的屬性和方法都會在物件範例間共用,無法做到範例私有。
  • 建構函式繼承:子類不能存取父類別原型上的方法。

組合繼承應運而生:

function SuperType(name){
     this.name = name;
     this.colors = ["red", "blue", "green"];
}
function SubType(name, age){
     SuperType.call(this, name) // 建構函式繼承 (兩次呼叫父類別建構函式)
     this.age = age;
}
SuperType.prototype.sayName = function() {
     console.log(this.name);
}
SubType.prototype = new SuperType() // 原型鏈繼承 (一次呼叫父類別建構函式)
SubType.prototype.sayAge = function() {
	 console.log(this.age);
}
let s1 = new SubType("Nicholas", 29)
let s2= new SubType("Greg", 27)
s1.colors.push("yellow")
console.log(s1.colors) // ['red', 'blue', 'green', 'yellow']
console.log(s2.colors) // ['red', 'blue', 'green']
s1.sayName() // Nicholas
s2.sayName() // Greg
s1.sayAge() // 29
s2.sayAge() // 27

但是呢?這樣做,會有效率問題,父類別建構函式始終會被呼叫兩次:一次是在子類建構函式中呼叫,另一次在是建立子類原型時呼叫。

本質上,子類原型最終是要包含超類物件的所有範例屬性,子類建構函式只要在執行時重寫自己的原型就行了。

這個時候有一個新的思路!

不通過呼叫父類別建構函式給子類原型賦值,而是取得父類別原型的一個副本。使用寄生式繼承來繼承父 類原型,然後將返回的新物件賦值給子類原型。

核心程式碼是:通過工廠的方式,增強一個新物件:

function createAnother(original){
     let clone = Object(original); // 通過呼叫函數建立一個新物件
     clone.sayHi = function() { // 以某種方式增強這個物件
         console.log("hi");
     };
     return clone; // 返回這個物件
}

將組合程式碼改造一下,完整程式碼是:

function inheritPrototype(subType, superType) {
     let prototype = Object(superType.prototype); // 建立物件
     prototype.constructor = subType; // 增強物件
     subType.prototype = prototype; // 賦值物件
}
function SuperType(name) {
     this.name = name;
     this.colors = ["red", "blue", "green"];
}
function SubType(name, age) {
     SuperType.call(this, name); // 建構函式繼承(只調了一次)
     this.age = age;
}
SuperType.prototype.sayName = function() {
     console.log(this.name);
};
inheritPrototype(SubType, SuperType); // 寄生繼承
SubType.prototype.sayAge = function() {
     console.log(this.age);
};
let s1 = new SubType("Nicholas", 29)
let s2= new SubType("Greg", 27)
s1.colors.push("yellow")
console.log(s1.colors) // ['red', 'blue', 'green', 'yellow']
console.log(s2.colors) // ['red', 'blue', 'green']
s1.sayName() // Nicholas
s2.sayName() // Greg
s1.sayAge() // 29
s2.sayAge() // 27

這裡只呼叫了一次 SuperType 建構函式,避免了 SubType.prototype 上不必要也用不到的屬性;而且,原型鏈仍然保持不變,instanceof 操作符和 isPrototypeOf() 方法正常有效。

寄生式組合繼承可以算是【參照型別】繼承的最佳模式

os:不過這裡的增強寫法,理解起來真是怪,為什麼父類別的顯示原型的建構函式等於子類?

SuperType.prototype.constructor=== SubType // true

大概是為了,通過寄生實現:父類別、子類都由同一函數構造;

SubType === SubType.prototype.constructor // true
SuperType.prototype.constructor === SubType.prototype.constructor // true

結語

只要是寫 JS 的繼承,一定離不開:工廠、構造、原型設計模式;

原型鏈 + 建構函式 = 組合繼承

工廠+建構函式 = 寄生繼承;

組合繼承和寄生繼承是最常用的兩種繼承方式。

......

u1s1,class 出來前,寫 JS 實現繼承,是真滴麻煩QAQ

以上就是JS繼承與工廠構造及原型設計模式詳解的詳細內容,更多關於JS繼承與設計模式的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com