mixin配合class实现多继承的绝佳妙用

时间: 2018-06-27阅读: 1042标签: class

什么是mixin

mixin一般翻译为“混入”、“混合”, 早期一般解释为:把一个对象的方法和属性拷贝到另一个对象上; 也可以简单理解为能够被继承的类, 最终目的是实现代码的复用。


从一个需求说起

为了使你能够最快的清楚我在说什么,我们从一个需求说起:

一个项目中有多个弹层需求; 一些是公共方法,比如点击关闭按钮关闭弹层; 一些弹层是可以拖动的,且有蒙层; 一些弹层是可以缩放的; 其他都是业务方法,无可复用性。

你可以先在心里想下,如果是你,你会怎样完成这个需求?


脑海中规划下

我们为公共方法写个类:BaseModal 为可拖动的弹层写个类:DragModal 为可缩放的弹层写个类:ScaleModal 为自定义的业务需求写个类:CustomModal,画个脑图的话,会是下面图片中的样子:

不同类之间的关系图


extends简单实现下

// 公共方法
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();
d.close(); // custom-close
c.drag(); // drag
c.do(); // do
c.hasLayer; // true


抛出问题

  • 如何使CustomModal能够同时继承DragModal和ScaleModal?
  • 某个相同方法希望不覆盖,而是都执行


试试早期的mixin方法实现多继承

// 可以拖动的弹层,我们写一个单独的类
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 extends DragModal {
  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类无法被继承


只继承不修改prototype的实现方式

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


存在的问题

这种方式不会修改父类的原型对象,但是如果存在跟父类同名的方法,只会执行父类的,而不回执行被继承的类的方法,那么如何使相同方法分别执行呢?


super实现相同方法不覆盖

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是一种思想,用来实现代码高度可复用性,又可以用来解决多继承的问题,是一种非常灵活的设计模式,如果你多多琢磨,相信你也会发现一些其他的妙用的,看好你哟!

原文来源:https://github.com/ronffy/mixin-class


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/899

关闭

ES6 class继承与super关键词深入探索

在ES6版本之前,JavaScript语言并没有传统面向对象语言的class写法,ES6发布之后,Babel迅速跟进,广大开发者也很快喜欢上ES6带来的新的编程体验。当然,在这门“混乱”而又精妙的语言中,许多每天出现我们视野中的东西却常常被我们忽略。

vue动态绑定class与style总结

在vue当中绑定class和style的方式有很多种,基本都知道,但是在项目当中真正遇到需要用样式变化呢的场景却怎么也想不起来,很模糊,只能写一些简单地样式逻辑,今天来总结一下vue中动态绑定样式的情况

classList介绍和原生JavaScript实现addClass、removeClass等

使用jQuery可以给元素很方便的添加class和删除class等操作,现在原生的JavaScript也可以实现这个方法了。使用classList可以方便的添加class、删除class、查询class等。elementClasses 是一个 DOMTokenList 表示 element 的类属性 。

css中class和id之间有什么区别?

我们平常在用DIV+CSS制作html网页页面时,常会用到class 和id来选择调用CSS样式属性。对学习CSS的新手来说class和id可能比较模糊,同时不知道什么时候该用class,什么时候又用id,以及它们用法与限制是怎么样的。

es6中class类的使用

在es5中我们是使用构造函数实例化出来一个对象,那么构造函数与普通的函数有什么区别呢?其实没有区别,无非就是函数名称用首字母大写来加以区分,这个不用对说对es5有了解的朋友都应该知道。

Class:向传统类模式转变的构造函数

JS基于原型的类,一直被转行前端的码僚们大呼惊奇,但接近传统模式使用class关键字定义的出现,ES6的class只是个语法糖,其定义生成的对象依然构造函数。不过为了与构造函数模式区分开,我们称其为类模式。

快速掌握ES6的class用法

先复习一下es5常用的构建类的方法:首先es5的写法使用原型进行对象的方法的,为什么不在构造函数里添加方法呢?因为实例化对象的时候,会重复的建立好多相同的方法,浪费资源。所以需要把对象的方法挂载到prtotype里。

ES6 class(类)

class (类)作为对象的模板被引入,可以通过 class 关键字定义类。类简要说明,类的本质是function,是基本原型继承的语法糖。所以,JS中继承的模型是不会被改变的。

ES6 static

ES6中新增了class这个语法糖。此class并非java中的class,ES6中的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。其中有static关键字。

css modules是什么?

所有的 class 的名称和动画的名称默认属于本地作用域的 CSS 文件。所以 CSS Modules 不是一个官方的规范,也不是浏览器的一种机制,它是一种构建步骤中的一个进程。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!