Mootools类定义增强因子

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

posted @ 2012-01-10 12:50  MrPrime  阅读(660)  评论(1编辑  收藏  举报