在C++和Lisp等传统编程语言中,Mixin是可以轻松被一个子类或一组子类集成功能的类,目的是为了函数的复用。

子类化

在了解深入研究Mixin和Decorator之前,需要大家先理解一下子类化这个术语。
子类化指针对一个新对象,从另一个对象中继承相关的属性。类B从另一个类A扩展得来,我们可以简单认为A是父类,B是A的一个子类。B的所有实例就继承了A的相关方法,但是B仍然能够定义自己特有的方法,以及重写A里定义的方法。
为了演示子类化,我们来看下面的代码例子。

  1. // 先构造一个人类的对象
  2. var Human = function (name, sex) {
  3. this.name = name
  4. this.sex = sex
  5. }
  6. // 我们可以这样简单的创建一个Human的实例
  7. var Tony_Stark = new Human("Tony Stark", "male")

我们再接着创建一个叫IronMan的子类,在拥有Human最基础的属性的基础上还有一些特殊的特征:

  1. // 钢铁侠子类
  2. var IronMan = function (name, sex, powers) {
  3. Human.call(this, name, sex)
  4. this.powers = powers
  5. }
  6. var ironMan = new IronMan("Tony Stark", "male", ["fly", "rich"])
  7. console.log(ironMan) // {name: "Tony Stark", sex: 'male', powers: Array(2)}

通过上述的子类化的方式,子类就拥有了父类的属性。

Mixin

在子类化中,子类获得了父类所有属性的继承。当我们只想继承父类的一部分属性(部分继承),或者从多个父类获得属性(多重继承)时,那么我们就可以利用Mixin来完成我们想要做的事情。
Javascript的对象原型非常适用于完成我们的需求。
我们先构建两个通用Mixin,如下:

  1. var myMixin1 = {
  2. moveup: function () {console.log('moveup')}
  3. }
  4. var myMinx2 = {
  5. movedown: function () {console.log('movedown')},
  6. stop: function () {console.log('stop')}
  7. }

我们创建一个函数,然后利用underscore.js的_.extend方法扩展函数的原型

  1. function carAnimator () {
  2. this.moveleft = function () {console.log('moveleft')}
  3. }
  4. _.extend(carAnimator.prototype, myMixin1)
  5. _.extend(carAnimator.prototype, myMixin2)
  6. var car = new carAnimator()
  7. car.moveleft() // moveleft
  8. car.moveup() // moveup
  9. car.stop() // stop

借此,我们就实现了多重继承的可复用需求。

实现多重继承+部分继承

我们现在不使用underscore的extend方法,来手动实现一个支持多重继承和部分继承的Mixin方法:

  1. var Car = function (setting) {
  2. this.model = setting.model
  3. this.color = setting.color
  4. }
  5. var myMixin1 = function () {}
  6. var myMixin2 = function () {}
  7. myMixin1.prototype = {
  8. moveup: function () {console.log('moveup')}
  9. }
  10. myMixin2.prototype = {
  11. movedown: function () {console.log('movedown')},
  12. stop: function () {console.log('stop')},
  13. start: function () {console.log('start')},
  14. }

Mixin方法实现:

  1. // sourceClass: 要扩展的对象,
  2. // mixinfunction: 可复用的mixin函数
  3. function Mixin (sourceClass, mixinfunction) {
  4. // 如果传入超过2个参数则认为是要实现部分继承,传入的参数表示要继承的方法名
  5. // 比如Mixin (Car, myMixin2, 'start', 'stop') 表示只需要继承myMixin2的start和stop方法
  6. if (arguments[2]) {
  7. for (var i =2; i < arguments.length; i++) {
  8. sourceClass.prototype[arguments[i]] = mixinfunction.prototype[arguments[i]]
  9. }
  10. } else {
  11. // 完全继承
  12. for (var method in mixinfunction.prototype) {
  13. sourceClass.prototype[method] = mixinfunction.prototype[method]
  14. }
  15. }
  16. }
  17. // 具体使用
  18. Mixin(Car, myMixin1)
  19. Mixin(Car, myMixin2, 'start', 'stop')
  20. var car = new Car({model: 'model', color: 'red'})
  21. car.start()
  22. car.moveup()
  23. car.stop()
  24. // 通过以上的mixin方法就实现了部分继承和多重继承

优点

Mixin模式有助于减少系统中的重复功能,增加函数复用。当一个应用程序可能需要在各种对象实例中共享行为时,我们可以通过Mixin专注于独立功能的开发。

缺点

引入的mixin会导致属性来源的不确定和原型污染。在大型系统中如果使用不当可能会提高代码维护的难度。