继承机制的出现,说明javascript已经到达大规模开发的门槛了。像java,为了应该其工业化,把设计模式发挥到臻美!有什么东西能逼使javascript的代码达到几千甚至上万行呢?除了框架类库都辅助工具外,那就是游戏这种奢侈的娱乐。光显示界面,操作界面,评估系统也能细分。另一个像EXT的那样利用纯javascript取代HTML与CSS的功能来开发网页,这也是很庞大的东西。庞大的东西就要模块化,虽然每一个模块未必都是一种类,但类是其不可或缺的组成元素。由于javascript的目的就是实现网页的交互,因此这些类大部分包含着操作DOM元素的内容,如生成一个节点啦,把它插入到DOM树的某处啦,设置它的样式,为它绑定事件,删除它……凡此种种。许多操作如果作为类的方法而存在,把它们命名更好的名字会更合适些。在各个类的联动方面,著名的观察者模式比浏览器提供的事件机制更具吸引力,起码在模块的解耦方面更胜一筹,如下面代码涉及三个模块:
this.keyboard
.onLayoutChange(this.progress.setLayout.bind(this.progress))
.onKeyPress(this.field.keyPressed.bind(this.field));
当键盘的外观发生改变后,上面的进度条也随之发生改变,当它的某个键被按下,显示区也相应发生响应。在暗地里,最后一个操作还会激发另一个操作:
keyPressed: function(key_code) {
var element = this.pullElement(key_code);
if (element) {
element.droppinFx.cancel();
element.highlight('#AFA', {
duration: 160, onFinish: element.remove.bind(element)
});
this.fire('hit', element.innerHTML);//★★★★
}
},
而用jQuery来模拟实际上很难实现:
this.settings.layouts.change(function(){
var layout = $(this).val();
var keynames = ths.keyboard.setLayout(layout);
ths.progress.setLayout(keynames)
});
第二个onKeyPress不是一个真正存在的事件,它是通过其他事件回调实现的。
hightlightKey: function(event) {
var key = this.keys[event.charCode] || this.keys[event.keyCode];
if(key){
if (!event.altKey && !event.ctrlKey && !event.metaKey) {
event.stop();
key.highlight({duration: 160});
this.fire('key_press', event.charCode || event.keyCode);//这里是onKeyPress
}
}
},
但这里的this并不是一个元素节点,事件机制对此无能为力,必需要引入观察者模式。如果真的需要这样设计,我们就要求类的存在了,至少在观察者模式中就要求有发布者对象与订阅者对象。mootools之所以能崛起,因为它提供了强大的继承机制,从而实现更复杂的编程。像EXT那样复杂的东西就更不在话下了。rightjs虽然不如上面提到的框架类库出名,但后生可畏,想必其作者也一定吸收了它们的优点。它的继承机制远远比它的前辈复杂强大,这正是我要学习它的原因。
var Class = function() {
//处理参数,一般有两个参数,第一个是父类,第二个是对象属性包
var args = $A(arguments), properties = args.pop() || {}, parent = args.pop();
// basic class object definition
//定义子类
function klass() {
return this.initialize ? this.initialize.apply(this, arguments) : this;
};
// 如果只指定了父类
if (!args.length && !isHash(properties)) {
parent = properties; properties = {};
}
// 为子类添加一些类方法,并继承父类
$ext(klass, Class.Methods).inherit(parent);
// catching the injections
//特殊处理属性包的extend、include方法
$w('extend include').each(function(name) {
if (properties[name]) {
var modules = properties[name];
klass[name].apply(klass, isArray(modules) ? modules : [modules]);
delete(properties[name]);
}
});
//混入属性包的其他方法或属性
return klass.include(properties);
};
样子长得同Prototypejs差不多,没办法,英雄所见略同,大家都爱拿Alex Arnell的实现来改良。里面有一个$ext方法:
//就是Prototype的extend,多了第三个参数,好像跟EXT学的
function $ext(dest, src, dont_overwrite) {//通常为undefined
var src = src || {}, key;
for (key in src)
if (dont_overwrite !== true || typeof(dest[key]) === 'undefined')
//如果为true检测目标对象是否已存在此属性,有就不添加了
dest[key] = src[key];
return dest;
};
印象中$开头的方法名定义在早期Prototypejs框架中很少出现,只有几个,自从给mootools抄了后,mootools把它发扬光大,反过来影响了Prototypejs。我讨厌这种命名方式 ,它当真把自己看成PHP吗?!
里面有一个isHash方法,用来检测其是否为javascript的Object对象,但不如jQuery的isPlainObject
isHash = function(value) {
return to_s.call(value) === '[object Object]';
};
//在IE中,可能无法识别元素节点等DOM对象,因此需要进一步处理
if (isHash(document.documentElement)) {//火狐 [object HTMLHtmlElement] IE [object Object]
isHash = function(value) {
return to_s.call(value) === '[object Object]' &&
value !== null && typeof(value) !== 'undefined' &&
typeof(value.hasOwnProperty) !== 'undefined';
};
}
jQuery的isPlainObject不但能区分DOM对象,还能区分那些自定义类。
isPlainObject: function( obj ) {
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) {
return false;
}
// Not own constructor property must be Object
if ( obj.constructor
&& !hasOwnProperty.call(obj, "constructor")
&& !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) {
return false;
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var key;
for ( key in obj ) {}
return key === undefined || hasOwnProperty.call( obj, key );
},
下一部分将讲解其Class.Methods方法,它为子类添加许多静态方法,才能继续下面的操作。