JS 继承:extend、mixin 和 plugin(三)
概述
前面讲过JS原型链的继承和扩展方式mixin的方式,2种方式都是直接作用到类本身上,虽然实现机制有差异,但是在运行时的上下文(context)也就是 this 引用是相同的,这样的继承方式无疑使最方便的,但是这里面有几个问题要解决:
1. 耦合性过高,因为使用的都是this上下文,所以所有的变量和私有函数(js本身不支持,但是可以约定)都有访问权,这样对组织和维护代码带来问题,父类内部逻辑的改变直接影响到本身。
2. 函数会相互覆盖,因为这2中方式最终都是将函数附加到类的原型链上,会出现意外的覆盖问题
3. 修改添加新功能成本高,进行功能扩展时需要对父类以及mixin有深入的了解,类的编写者问题不大,但是其他人员来扩展时就非常麻烦。
plugin的机制
一个JS类(控件)于它的Plugin的关系仅仅是依赖关系,plugin不了解控件的内部实现,只了解类的公共接口和公共属性,只是通过这些公共属性和方法去操纵控件。这样只要控件的接口保持不变,它的plugin始终是可用的,降低了耦合性。我们来看一下plugin的简单实现:
//这里仅仅是示例
function Grid(config){
this.plugins = config.plugins;
//初始化插件
this.plugins = initPlugins(plugins);
//To Do
}
Grid.prototype = {
//初始化
initPlugins : function(plugins){
var result = [];
$.each(plugins,function(index,plugin)){
//可以判断plugin是否实例化...
var p = new plugin();
p.initializer(this);
//To Do something
result.push(p);
}
return result;
},
//绑定事件时
bindUI : function(){
//and so on
$.each(this.plugins,function(index,plugin)){
plugin.bindUI(this);
}
}
};
//类B是A的插件
function B(){
}
B.prototype = {
//初始化方法接收A的示例
initializer : function(grid){
this.grid = grid;
....
},
bindUI : function(){
var grid = this.grid;
//To do
grid.on('cellclick',function(){});
}
}
//类B是A的插件
function C(){
}
var a = new A({
plugins:[B,C]
});
这里只是一个简单的实现,前面我讲过控件的生命周期,在每个周期控件都会调用自己的plugins进行相应的操作,例如:创建DOM、绑定事件以及销毁。
plugin和mixin
plugin和mixin 都是对控件的一种扩展方式,在最前面我们也讲到了一些差别,概括起来:
1. 相同的地方:
1) 都是对控件进行扩展
2) 整个生命周期都受控件管理
2. 不同的地方:
1) 上下文不同,也就是 this 引用不同
2) 对控件的操作权限不同,mixin能控制控件所有的变量和函数,plugin只能处理公用方法和变量
3) mixin是抽象层次的,行为上像一个抽象类,可以由多个控件来使用,而且多个控件之间没有继承上的关系。
4) plugin是实现层次的,是一个适配器,只能作用于一个控件或者其子类。
拿具体的实例来说:
例如,拖拽功能,我们可以把它做成控件的一个扩展 mixin,只要指定了要拖动的位置,就可以拖动这个控件,不同的控件都可以使用。
例如,一个级联的Grid,我给Grid写的plugin 只能作用在Grid及其子类上。
总结
JS类的继承方式我已经讲完了,这3种方式都非常有用,各有其适应环境,一套控件如果都有支持这几种机制,那么它的扩展性就非常的好,后面我会拿具体的实例来说明。
我想把自己基于jquery的一套UI库作为示例,来讲解如何写一套易于扩展、易用的JS控件。
不知道各位最感兴趣的是哪个控件。