佛经里有句话,叫功不唐捐。意思是所有努力的效果不一定产生在当下,可能产生在未来,但你只要做了,它一定就有意义。我们心态平和一点,逐步去推进它,逐步去改良它,对未来必定很有意义。
有人总很狭隘地支持某一类库或框架,其实好的东西都是互相渗透的。像jQuery,它提供了对DOM的操作,但仅仅这样是不够的。请不要用后台的增删改查的目光来看前台,前台的展现是非常丰富多彩的。后台也不是那么枯燥,当然对无能的人怎么也一样。他们总是有借口逃避学习,总是问人来解决项目上的问题。这样的IT生涯真的只能撑到30岁。我们专注一个东西,其他同类型的产品都是非常有参考价值,能扩宽我们的思维。比如最早称霸javascript界的Prototype.js,它是作为rails的一个辅助项目诞生。借鉴于真正的一切都是对象的ruby的设计,Prototype.js当年横扫天下,无人可敌。我们可以看到其许多方法名都是取自ruby标准库的。像Base2这样强大的库,我觉得它也从Prototype.js吸取了不少灵感。mootools号称是Prototype.js,框架是Prototype,但许多实现却改自Base2。这三者都拥有强大的继承机制,反正学了其中一个,对其他两个就很容易上手。换言之,功不唐捐,你的智慧投资不会白费的。
西方的开源世界非常活跃,新兴类库都是有着浓厚的已知框架的影子。rightjs就是一个例子。今天讲解其两个模块,不由得感概万分。
Options模块,专门来处理参数。在Prototype著名的特效库中,已经有这种迹象了。mootools干脆交由Object.reset来做这粗活。为什么要这样做呢?因为通常我们要传入一个属性包来配置新生成的类,为它添加类成员,实例成员什么的,在mootools2的蓝图中,甚至计划配置友元成员。这个其实在mootools1.2.4中已用protect方法实现了,不过它想搞得更smart一些。
Options = {
setOptions: function(options) {
//整合两个对象的options属性,一个是配置用的属性包,一个是新类,注意Options模块是不能独立运作的,
//它肯定用于其他模块或类中。
var options = this.options = Object.merge(Class.findSet(this, 'options'), options), match, key;
//如果它的on属性为函数,执行方法,如this.on("click",function(){})
if (isFunction(this.on)) {
for (key in options) {
if (match = key.match(/on([A-Z][A-Za-z]+)/)) {
this.on(match[1].toLowerCase(), options[key]);
delete(options[key]);
}
}
}
return this;
},
//由于rightjs的类工厂最多只有两个参数,父类与作为属性包的对象,现在的目的是取得那个属性包
cutOptions: function(args) {
var args = $A(args);
this.setOptions(isHash(args.last()) ? args.pop() : {});
return args;
}
};
下面是观察者模块,它的许多方法都支持多种传参形式,像jQuery一样,把逻辑搞得很复杂,实在恶心。
Observer = new Class({
include: Options,
/**
* general constructor
*
* @param Object options
*/
initialize: function(options) {
this.setOptions(options);
Observer.createShortcuts(this, Class.findSet(this, 'events'));
},
//this.on("click", fnCallback)
//添加回调函数与其事件名,额外参数
on: function() {
var args = $A(arguments), event = args.shift();
if (isString(event)) {
//创建一个$listeners存放一个个特别的对象
if (!defined(this.$listeners)) this.$listeners = [];
var callback = args.shift(), name;
switch (typeof callback) {
case "string":
name = callback;
callback = this[callback];
case "function":
var hash = {};
// DON'T move it in the one-line hash variable definition,
// it causes problems with the Konqueror 3 later on
hash.e = event;
hash.f = callback;
hash.a = args;
hash.r = name;
this.$listeners.push(hash);
break;
default:
if (isArray(callback)) {
for (var i=0; i < callback.length; i++) {
//看它是否为二维数组
this.on.apply(this, [event].concat(
isArray(callback[i]) ? callback[i] : [callback[i]]
).concat(args));
}
}
}
} else {
//处理参数只有一个属性包的情形
for (var name in event) {
this.on.apply(this, [name].concat(
isArray(event[name]) ? event[name] : [event[name]]
).concat(args));
}
}
return this;
},
//没有好说的,与Prototype的observes差不多
observes: function(event, callback) {
if (!isString(event)) { callback = event; event = null; }
if (isString(callback)) callback = this[callback];
return (this.$listeners || []).some(function(i) {
return (event && callback) ? i.e === event && i.f === callback :
event ? i.e === event : i.f === callback;
});
},
stopObserving: function(event, callback) {
if (isHash(event)) {
for (var key in event) {
this.stopObserving(key, event[key]);
}
} else {
if (!isString(event)) { callback = event; event = null; }
if (isString(callback)) callback = this[callback];
this.$listeners = (this.$listeners || []).filter(function(i) {
return (event && callback) ? (i.e !== event || i.f !== callback) :
(event ? i.e !== event : i.f !== callback);
}, this);
}
return this;
},
//用于返回不重复回调函数,如果不指明事件名则返回全部
listeners: function(event) {
return (this.$listeners || []).filter(function(i) {
return !event || i.e === event;
}).map(function(i) { return i.f; }).uniq();
},
//强制执行其对象上的事件
fire: function() {
var args = $A(arguments), event = args.shift();
(this.$listeners || []).each(function(i) {
if (i.e === event) i.f.apply(this, i.a.concat(args));
}, this);
return this;
},
extend: {
//让所有对象都具有观察者模式
create: function(object, events) {
$ext(object, Object.without(this.prototype, 'initialize', 'setOptions'), true);
return this.createShortcuts(object, events || Class.findSet(object, 'events'));
},
createShortcuts: function(object, names) {
//为目标对象object扩展像onLayoutChange的自定义事件
(names || []).each(function(name) {
var method_name = 'on'+name.replace(/:/g, '_').camelize().capitalize();
if (!defined(object[method_name])) {
object[method_name] = function() {
return this.on.apply(this, [name].concat($A(arguments)));
};
}
});
return object;
}
}
});
//对observe进行别名
$alias(Observer.prototype, { observe: 'on' });
观察者模式让自定类也拥有像事件系统的关联操作,一变大家跟着变,非常有利于解耦。从最初的Class到Options到Observer,它把能拆解的东西都拆解了。这种模块化设计非常有利于我们学习。像jQuery,它走的是另一条路,通过大量的插件来弥补其核心库的功能。当然,人多好办事,它也不时吸纳一些优秀的方法加入核心库了。但无论怎么折腾,类库的能力还是很有限,与这种全面走扩展道路的框架没得比。与对于学习javascript来说,像Prototype这样的框架能让你收获更大。