在C++和Lisp等传统编程语言中,Mixin是可以轻松被一个子类或一组子类集成功能的类,目的是为了函数的复用。
子类化
在了解深入研究Mixin和Decorator之前,需要大家先理解一下子类化这个术语。
子类化指针对一个新对象,从另一个对象中继承相关的属性。类B从另一个类A扩展得来,我们可以简单认为A是父类,B是A的一个子类。B的所有实例就继承了A的相关方法,但是B仍然能够定义自己特有的方法,以及重写A里定义的方法。
为了演示子类化,我们来看下面的代码例子。
// 先构造一个人类的对象
var Human = function (name, sex) {
this.name = name
this.sex = sex
}
// 我们可以这样简单的创建一个Human的实例
var Tony_Stark = new Human("Tony Stark", "male")
我们再接着创建一个叫IronMan的子类,在拥有Human最基础的属性的基础上还有一些特殊的特征:
// 钢铁侠子类
var IronMan = function (name, sex, powers) {
Human.call(this, name, sex)
this.powers = powers
}
var ironMan = new IronMan("Tony Stark", "male", ["fly", "rich"])
console.log(ironMan) // {name: "Tony Stark", sex: 'male', powers: Array(2)}
Mixin
在子类化中,子类获得了父类所有属性的继承。当我们只想继承父类的一部分属性(部分继承),或者从多个父类获得属性(多重继承)时,那么我们就可以利用Mixin来完成我们想要做的事情。
Javascript的对象原型非常适用于完成我们的需求。
我们先构建两个通用Mixin,如下:
var myMixin1 = {
moveup: function () {console.log('moveup')}
}
var myMinx2 = {
movedown: function () {console.log('movedown')},
stop: function () {console.log('stop')}
}
我们创建一个函数,然后利用underscore.js的_.extend方法扩展函数的原型
function carAnimator () {
this.moveleft = function () {console.log('moveleft')}
}
_.extend(carAnimator.prototype, myMixin1)
_.extend(carAnimator.prototype, myMixin2)
var car = new carAnimator()
car.moveleft() // moveleft
car.moveup() // moveup
car.stop() // stop
实现多重继承+部分继承
我们现在不使用underscore的extend方法,来手动实现一个支持多重继承和部分继承的Mixin方法:
var Car = function (setting) {
this.model = setting.model
this.color = setting.color
}
var myMixin1 = function () {}
var myMixin2 = function () {}
myMixin1.prototype = {
moveup: function () {console.log('moveup')}
}
myMixin2.prototype = {
movedown: function () {console.log('movedown')},
stop: function () {console.log('stop')},
start: function () {console.log('start')},
}
Mixin方法实现:
// sourceClass: 要扩展的对象,
// mixinfunction: 可复用的mixin函数
function Mixin (sourceClass, mixinfunction) {
// 如果传入超过2个参数则认为是要实现部分继承,传入的参数表示要继承的方法名
// 比如Mixin (Car, myMixin2, 'start', 'stop') 表示只需要继承myMixin2的start和stop方法
if (arguments[2]) {
for (var i =2; i < arguments.length; i++) {
sourceClass.prototype[arguments[i]] = mixinfunction.prototype[arguments[i]]
}
} else {
// 完全继承
for (var method in mixinfunction.prototype) {
sourceClass.prototype[method] = mixinfunction.prototype[method]
}
}
}
// 具体使用
Mixin(Car, myMixin1)
Mixin(Car, myMixin2, 'start', 'stop')
var car = new Car({model: 'model', color: 'red'})
car.start()
car.moveup()
car.stop()
// 通过以上的mixin方法就实现了部分继承和多重继承
优点
Mixin模式有助于减少系统中的重复功能,增加函数复用。当一个应用程序可能需要在各种对象实例中共享行为时,我们可以通过Mixin专注于独立功能的开发。
缺点
引入的mixin会导致属性来源的不确定和原型污染。在大型系统中如果使用不当可能会提高代码维护的难度。