最近研习《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。希望也能很好的发展。