什么是mixin
mixin一般翻译为“混入”、“混合”,
早期一般解释为:把一个对象的方法和属性拷贝到另一个对象上;
也可以简单理解为能够被继承的类,
最终目的是实现代码的复用。
从一个需求说起
为了使你能够最快的清楚我在说什么,我们从一个需求说起:
一个项目中有多个弹层需求;
一些是公共方法,比如点击关闭按钮关闭弹层;
一些弹层是可以拖动的,且有蒙层;
一些弹层是可以缩放的;
其他都是业务方法,无可复用性。
你可以先在心里想下,如果是你,你会怎样完成这个需求?
脑海中规划下
我们为公共方法写个类:BaseModal
为可拖动的弹层写个类:DragModal
为可缩放的弹层写个类:ScaleModal
为自定义的业务需求写个类:CustomModal
画个脑图的话,会是下面图片中的样子:
extends简单实现下
看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| 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(); c.drag(); c.do(); c.hasLayer;
|
抛出问题
- 如何使
CustomModal
能够同时继承DragModal
和ScaleModal
?
- 某个相同方法希望不覆盖,而是都执行
试试早期的mixin方法实现多继承
看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| 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(); c.drag(); c.scale(); console.log(c.hasLayer);
|
存在的问题
以上mix
方式实现了多继承,但存在以下问题
- 会修改
target
类的原型对象
target
类的相同方法名会被被继承类的相同方法名覆盖
- 实例属性无法继承
BaseModal
类无法被继承
只继承不修改prototype的实现方式
看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 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(); c.drag(); console.log(c.hasLayer);
|
存在的问题
如何让CustomModal
再继承ScaleModal
呢?
其实很简单,在上面基础上,我们再写一个ScaleModalMixinMixin
类就可以了
完美的多继承
看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 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(); c.drag(); c.scale(); console.log(c.hasLayer);
|
存在的问题
这种方式不会修改父类的原型对象,但是如果存在跟父类同名的方法,只会执行父类的,而不回执行被继承的类的方法,那么如何使相同方法分别执行呢?
super实现相同方法不覆盖
看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| 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();
|
总结
Mixin是一种思想,用来实现代码高度可复用性,又可以用来解决多继承的问题,是一种非常灵活的设计模式,如果你多多琢磨,相信你也会发现一些其他的妙用的,看好你哟!