Github 源碼地址javascript
mixin通常翻譯爲「混入」、「混合」,
早期通常解釋爲:把一個對象的方法和屬性拷貝到另外一個對象上;
也能夠簡單理解爲可以被繼承的類,
最終目的是實現代碼的複用。java
爲了使你可以最快的清楚我在說什麼,咱們從一個需求提及:git
一個項目中有多個彈層需求;
一些是公共方法,好比點擊關閉按鈕關閉彈層;
一些彈層是能夠拖動的,且有蒙層;
一些彈層是能夠縮放的;
其餘都是業務方法,無可複用性。github
你能夠先在內心想下,若是是你,你會怎樣完成這個需求?設計模式
咱們爲公共方法寫個類:BaseModal
爲可拖動的彈層寫個類:DragModal
爲可縮放的彈層寫個類:ScaleModal
爲自定義的業務需求寫個類:CustomModal
spa
畫個腦圖的話,會是下面圖片中的樣子:prototype
// 公共方法 class BaseModal { close(){ console.log('close'); } } // 能夠拖動的彈層,咱們寫一個單獨的類 class DragModal extends BaseModal { hasLayer = true; drag() { console.log('drag'); } } // 能夠縮放的彈層,咱們寫一個單獨的類 class ScaleModal extends BaseModal { scale() { console.log('scale'); } } // 業務方法 class CustomModal extends DragModal { close(){ console.log('custom-close'); } do() { console.log('do'); } } let c = new CustomModal(); c.close(); // custom-close c.drag(); // drag c.do(); // do c.hasLayer; // true
CustomModal
可以同時繼承DragModal
和ScaleModal
?// 能夠拖動的彈層,咱們寫一個單獨的類 class DragModal extends BaseModal { hasLayer = true; drag() { console.log('drag'); } } // 能夠縮放的彈層,咱們寫一個單獨的類 class ScaleModal extends BaseModal { scale() { console.log('scale'); } } // 獲取原型對象的全部屬性和方法 function getPrototypes(ClassPrototype) { return Object.getOwnPropertyNames(ClassPrototype).slice(1); } function mix(...mixins){ return function(target){ if (!mixins || !Array.isArray(mixins)) return target; let cp = target.prototype; for (let C of mixins) { let mp = C.prototype; for (let m of getPrototypes(mp)) { cp[m] = mp[m]; } } } } @mix(DragModal, ScaleModal) class CustomModal { scale(){ console.log('custom-scale'); } do() { console.log('do'); } } let c = new CustomModal(); c.close(); // 報錯,由於dobase沒在A或B的prototype上,而是在A.prototype.__proto__上 c.drag(); // drag c.scale(); // scale 並不是是咱們想要的custom-scale console.log(c.hasLayer); // undefined
以上mix
方式實現了多繼承,但存在如下問題翻譯
target
類的原型對象target
類的相同方法名會被被繼承類的相同方法名覆蓋BaseModal
類沒法被繼承class BaseModal { close() { console.log('close'); } } let DragModalMixin = (extendsClass) => class extends extendsClass { hasLayer = true; drag() { console.log('drag'); } }; class CustomModal extends DragModalMixin(BaseModal) { drag() { console.log('custom-drag'); } do() { console.log('do'); } } let c = new CustomModal(); c.close(); // close c.drag(); // custom-drag console.log(c.hasLayer); // true
如何讓CustomModal
再繼承ScaleModal
呢?
其實很簡單,在上面基礎上,咱們再寫一個ScaleModalMixinMixin
類就能夠了設計
class BaseModal { close() { console.log('close'); } } let DragModalMixin = (extendsClass) => class extends extendsClass { hasLayer = true; drag() { console.log('drag'); } }; let ScaleModalMixin = (extendsClass) => class extends extendsClass { scale() { console.log('scale'); } }; class CustomModal extends ScaleModalMixin(DragModalMixin(BaseModal)) { drag() { console.log('custom-drag'); } do() { console.log('do'); } } let c = new CustomModal(); c.close(); // close c.drag(); // custom-drag c.scale(); // scale console.log(c.hasLayer); // true
這種方式不會修改父類的原型對象,可是若是存在跟父類同名的方法,只會執行父類的,而不回執行被繼承的類的方法,那麼如何使相同方法分別執行呢?code
class BaseModal { close() { console.log('close'); } } let DragModalMixin = (extendsClass) => class extends extendsClass { hasLayer = true; drag() { console.log('drag'); } }; let ScaleModalMixin = (extendsClass) => class extends extendsClass { scale() { console.log('scale'); } close() { console.log('scale-close'); if (super.close) super.close(); } }; class CustomModal extends ScaleModalMixin(DragModalMixin(BaseModal)) { close() { console.log('custom-close'); if (super.close) super.close(); } do() { console.log('do'); } } let c = new CustomModal(); c.close(); // custom-close -> scale-close -> close
Mixin是一種思想,用來實現代碼高度可複用性,又能夠用來解決多繼承的問題,是一種很是靈活的設計模式,若是你多多琢磨,相信你也會發現一些其餘的妙用的,看好你喲!