最近研习《Pro Javascript with Mootools》,跟jQuery比起来,Mootools的资料算是少之又少的(Mootools在用户推广和社区建设方面有必要向jQuery学习下)。这本书比较全面的对Mootools-core中各个关键模块做了说明和分析,每章都由原生javascript逐渐引入,总之是本不错的书,虽然现在jQuery在业界大红大紫, 但个人也希望mootools能得到不错的发展。
回到正题,Mootools跟jQuery一个比较显著的差异就是在代码组织方式上,Mootools引入了OOP思想,实现了功能强大的Class类。
Mootools默认内置了两个Class的增强因子,一个是Extends,另外一个是Implements.
有一定Mootools使用经验的园友应该很熟悉这两个扩展,内部都用于实现javascript的prototype链继承(但是不仅仅如此)。
不同的是Extends模拟OOP中子类继承父类的操作,Implements的话可以理解成继承方法(和Java或者C#中实现接口有些不同,接口只是函数声明,没有定义)。
下面个例子:
var Human = new Class({ // 初始化方法 initialize: function(name, gender) { this.name = name; this.gender = gender; }, toString: function() { return 'name:' + this.name + ',' + 'gender:' + this.gender; } }); var Behavior = new Class({ eat: function() { console.log(this.name + ' wanna grab something to eat'); }, sleep: function() { console.log(this.name + ' wanna sleep'); }, haveFun: function() { console.log(this.name + ' wanna have some fun'); } }); var Developer = new Class({ Extends: Human, Implements: Behavior, initialize: function(name, gender, lang) { this.parent(name, gender); //调用父类中对应的方法 this.lang = 'javascript'; }, toString: function() { return this.parent() + ',language:' + this.lang; } }); var Andrew = new Developer('Andrew', 'male', 'javascript'); console.log(Andrew.toString()); // name:Andrew,gender:male,language:javascript Andrew.eat(); // Andrew wanna grab something to eat Andrew.sleep(); // Andrew wanna sleep Andrew.haveFun(); // Andrew wanna have some fun
从这个例子出应该不难看出Extends和Implements所起到的作用,如果你像我一样对其中的实现细节很感兴趣,可以研究下Mootools中的Class模块,一开始去看源码确实有点困难,硬着头皮多看几遍就慢慢有点思路了。这里有篇园友的随笔,他对Class模块做了一些注释,足够了解其运行机制。点击浏览此文,感谢苦苦的苦瓜。
除了基本的Extends和Implements,我们还可以实现自定义的增强因子,从源码中我们可以看出,Class模块的增强因子通过Class.Mutators进行扩展。下面给出源码,我们可以有个参照。
Class.Mutators = { Extends: function(parent){ this.parent = parent; this.prototype = getInstance(parent); }, Implements: function(items){ Array.from(items).each(function(item){ var instance = new item; for (var key in instance) implement.call(this, key, instance[key], true); }, this); } };
一下子看不大明白很正常,如果要分析源码,篇幅会很长,这里其实只要搞清一些注意点就行. 结合之前的Developer例子看,Class.Mutators.Extends执行时,其中this指的就是构造类Developer(记得吗,我们通过new Developer来产生一个实例),其中parent就是继承的Human构造类,getInstance(parent)返回Human的实例,这里很明显就是基于prototype的继承实现(但不仅如此,就这块代码而言是这样)。再简单的看下Class.Mutators.Implements,运行时通过其中的implement方法给Developer的prototype扩展方法或属性。
现在来个自定义的例子,我们自己动手干, 来实现一个自动实现属性getter,setter方法:
// mootools Mutator GetterSetter Class.Mutators.GetterSetter = function(props) { var klass = this; Array.from(props).each(function(prop) { var capProp = prop.capitalize(); klass.implement('set' + capProp, function(value) { this[prop] = value; return this; }); klass.implement('get' + capProp, function(value) { return this[prop]; }); }); };
如果我们把GetterSetter用于Developer构造类的定义,那么Class.Mutators.GetterSetter执行时,this指的就是Developer。我们看一下each里面的回调,通过闭包klass维持着对Developer的引用,通过implement方法在其prototype上扩展属性的setter,getter方法。这样Developer的实例就自动拥有了类似Java类中的setter,getter。通过扩展Class.Mutators,我们优雅的实现了这个功能。来验证一下:
var Developer = new Class({ Extends: Human, Implements: Behavior, GetterSetter: ['name', 'gender', 'lang'], initialize: function(name, gender, lang) { this.parent(name, gender); this.lang = 'javascript'; }, toString: function() { return this.parent() + ',language:' + this.lang; } }); var Andrew = new Developer('Andrew', 'male', 'javascript'); console.log(Andrew.getName()); // Andrew console.log(Andrew.getGender()); // male console.log(Andrew.getLang()); // javascript
个人觉得很强大,再来一个实现模拟静态属性(static)的例子:
Class.Mutators.Static = function(props) { this.extend(props); };
看起来再简单不过了,不是么?我们再来验证一下:
var Developer = new Class({ Static: { count: 0, addCount: function() { Developer.count++; } }, Extends: Human, Implements: Behavior, initialize: function(name, gender, lang) { this.parent(name, gender); this.lang = 'javascript'; Developer.addCount(); }, toString: function() { return this.parent() + ',language:' + this.lang; } }); var Andrew = new Developer('Andrew', 'male', 'javascript'); var Rocky = new Developer('Rocky', 'male', 'php'); console.log(Developer.count); // 2
我们通过count给实例化的对象计数,或许你会觉得count和addCount就这么暴露给外部是个问题,我们可以用匿名函数来模拟实现private:
var Developer = (function() { // private variable or function var count = 0, addCount = function() { count++; }; return new Class({ Static: { getCount: function() { return count; } }, Extends: Human, Implements: Behavior, initialize: function(name, gender, lang) { this.parent(name, gender); this.lang = 'javascript'; addCount(); }, toString: function() { return this.parent() + ',language:' + this.lang; } }); })(); var Andrew = new Developer('Andrew', 'male', 'javascript'); var Rocky = new Developer('Rocky', 'male', 'php'); console.log(Developer.count); // undefined console.log(Developer.addCount()); // undefined console.log(Developer.getCount()); // 2
注意到,我们利用匿名函数和闭包特性模拟实现了count和addCount的private,对比之前外部可以通过Developer.count或者Developer.addCount直接改变计数显得安全的多了。
最后就jQuery和Mootools,我想再稍微吐糟一下。
jQuery为何如此流行:它起点低,入门简单,不怎么懂javascript的程序员或者设计师,都可以简单上手,而且它的文档做的很不错,用户推广做的好,社区活跃,不可否认jQuery形势一片大好,在结合BackBone等MVC framework的辅助,真是不错。
Mootools虽然几乎和jQuery同时出现,但是其社区规模和受欢迎程度确实没有jQuery高,从github上watch和fork人数就可见一斑。一方面Mootools的上手确实不太适合javascript初学者,另一方面对于很多程序员来讲,貌似并不需要Mootools的对OOP的实现,而jQuery对于DOM操作和特效的实现更为简单...所以,Mootools现在的情况有点小尴尬,不过它依然是一个很好的framework。希望也能很好的发展。