JS繼承與工廠構造及原型設計模式詳解
序言
我們在前一篇文章《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繼承與設計模式的資料請關註WalkonNet其它相關文章!