Prototype源码分析1
非原创,原始链接为http://hi.baidu.com/gottwolf/blog/item/d13240d1edc1763e9a5027a8.html
/**
*定义一个全局对象,属性Version在发布的时候会替换为当前版本号
*/
varPrototype={
Version:'@@VERSION@@'
}
/**
*创建一种类型,注意其属性create是一个方法,返回一个构造函数。
*一般使用如下
*varX=Class.create();返回一个类型,类似于java的一个Class实例。
*要使用X类型,需继续用newX()来获取一个实例,如同java的Class.newInstance()方法。
*
*返回的构造函数会执行名为initialize的方法,initialize是Ruby对象的构造器方法名字。
*此时initialize方法还没有定义,其后的代码中创建新类型时会建立相应的同名方法。
*
*如果一定要从java上去理解。你可以理解为用Class.create()创建一个继承java.lang.Class类的类。当然java不允许这样做,因为Class类是final的
*
*/
varClass={
create:function(){
returnfunction(){
this.initialize.apply(this,arguments);
}
}
}
/**
*创建一个对象,从变量名来思考,本意也许是定义一个抽象类,以后创建新对象都extend它。
*但从其后代码的应用来看,Abstract更多是为了保持命名空间清晰的考虑。
*也就是说,我们可以给Abstract这个对象实例添加新的对象定义。
*
*从java去理解,就是动态给一个对象创建内部类。
*/
varAbstract=newObject();
/**
*获取参数对象的所有属性和方法,有点象多重继承。但是这种继承是动态获得的。
*如:
*vara=newObjectA(),b=newObjectB();
*varc=a.extend(b);
*此时c对象同时拥有a和b对象的属性和方法。但是与多重继承不同的是,cinstanceofObjectB将返回false。
*/
Object.prototype.extend=function(object){
for(propertyinobject){
this[property]=object[property];
}
returnthis;
}
/**
*这个方法很有趣,它封装一个javascript函数对象,返回一个新函数对象,新函数对象的主体和原对象相同,但是bind()方法参数将被用作当前对象的对象。
*也就是说新函数中的this引用被改变为参数提供的对象。
*比如:
*<inputtype="text"id="aaa"value="aaa">
*<inputtype="text"id="bbb"value="bbb">
*..
*<script>
*varaaa=document.getElementById("aaa");
*varbbb=document.getElementById("bbb");
*aaa.showValue=function(){alert(this.value);}
*aaa.showValue2=aaa.showValue.bind(bbb);
*</script>
*那么,调用aaa.showValue将返回"aaa",但调用aaa.showValue2将返回"bbb"。
*
*apply是ie5.5后才出现的新方法(Netscape好像很早就支持了)。
*该方法更多的资料参考MSDNhttp://msdn.microsoft.com/library/en-us/script56/html/js56jsmthApply.asp
*还有一个call方法,应用起来和apply类似。可以一起研究下。
*/
Function.prototype.bind=function(object){
varmethod=this;
returnfunction(){
method.apply(object,arguments);
}
}
/**
*和bind一样,不过这个方法一般用做html控件对象的事件处理。所以要传递event对象
*注意这时候,用到了Function.call。它与Function.apply的不同好像仅仅是对参数形式的定义。
*如同java两个过载的方法。
*/
Function.prototype.bindAsEventListener=function(object){
varmethod=this;
returnfunction(event){
method.call(object,event||window.event);
}
}
/**
*将整数形式RGB颜色值转换为HEX形式
*/
Number.prototype.toColorPart=function(){
vardigits=this.toString(16);
if(this<16)return'0'+digits;
returndigits;
}
/**
*典型Ruby风格的函数,将参数中的方法逐个调用,返回第一个成功执行的方法的返回值
*/
varTry={
these:function(){
varreturnValue;
for(vari=0;i<arguments.length;i++){
varlambda=arguments[i];
try{
returnValue=lambda();
break;
}catch(e){}
}
returnreturnValue;
}
}
/*--------------------------------------------------------------------------*/
/**
*一个设计精巧的定时执行器
*首先由Class.create()创建一个PeriodicalExecuter类型,
*然后用对象直接量的语法形式设置原型。
*
*需要特别说明的是rgisterCallback方法,它调用上面定义的函数原型方法bind,并传递自己为参数。
*之所以这样做,是因为setTimeout默认总以window对象为当前对象,也就是说,如果registerCallback方法定义如下的话:
*registerCallback:function(){
*setTimeout(this.onTimerEvent,this.frequency*1000);
*}
*那么,this.onTimeoutEvent方法执行失败,因为它无法访问this.currentlyExecuting属性。
*而使用了bind以后,该方法才能正确的找到this,也就是PeriodicalExecuter的当前实例。
*/
varPeriodicalExecuter=Class.create();
PeriodicalExecuter.prototype={
initialize:function(callback,frequency){
this.callback=callback;
this.frequency=frequency;
this.currentlyExecuting=false;
this.registerCallback();
},
registerCallback:function(){
setTimeout(this.onTimerEvent.bind(this),this.frequency*1000);
},
onTimerEvent:function(){
if(!this.currentlyExecuting){
try{
this.currentlyExecuting=true;
this.callback();
}finally{
this.currentlyExecuting=false;
}
}
this.registerCallback();
}
}
/*--------------------------------------------------------------------------*/
/**
*这个函数就Ruby了。我觉得它的作用主要有两个
*1.大概是document.getElementById(id)的最简化调用。
*比如:$("aaa")将返回上aaa对象
*2.得到对象数组
*比如:$("aaa","bbb")返回一个包括id为"aaa"和"bbb"两个input控件对象的数组。
*/
function$(){
varelements=newArray();
for(vari=0;i<arguments.length;i++){
varelement=arguments[i];
if(typeofelement=='string')
element=document.getElementById(element);
if(arguments.length==1)
returnelement;
elements.push(element);
}
returnelements;
}
/**
*定义Ajax对象,静态方法getTransport方法返回一个XMLHttp对象
*/
varAjax={
getTransport:function(){
returnTry.these(
function(){returnnewActiveXObject('Msxml2.XMLHTTP')},
function(){returnnewActiveXObject('Microsoft.XMLHTTP')},
function(){returnnewXMLHttpRequest()}
)||false;
},
emptyFunction:function(){}
}
/**
*我以为此时的Ajax对象起到命名空间的作用。
*Ajax.Base声明为一个基础对象类型
*注意Ajax.Base并没有使用Class.create()的方式来创建,我想是因为作者并不希望Ajax.Base被库使用者实例化。
*作者在其他对象类型的声明中,将会继承于它。
*就好像java中的私有抽象类
*/
AJAX.Base=function(){};
AJAX.Base.prototype={
/**
*extend(见prototype.js中的定义)的用法真是让人耳目一新
*options首先设置默认属性,然后再extend参数对象,那么参数对象中也有同名的属性,那么就覆盖默认属性值。
*想想如果我写这样的实现,应该类似如下:
setOptions:function(options){
this.options.methed=options.methed?options.methed:'post';
.
}
我想很多时候,java限制了js的创意。
*/
setOptions:function(options){
this.options={
method:'post',
asynchronous:true,
parameters:''
}.extend(options||{});
}
}
/**
*Ajax.Request封装XmlHttp
*/
AJAX.Request=Class.create();
/**
*定义四种事件(状态),参考http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/readystate_1.asp
*/
AJAX.Request.Events=
['Uninitialized','Loading','Loaded','Interactive','Complete'];
/**
*
*/
AJAX.Request.prototype=(newAjax.Base()).extend({
initialize:function(url,options){
this.transport=Ajax.getTransport();
this.setOptions(options);
try{
if(this.options.method=='get')
url+='?'+this.options.parameters+'&_=';
/**
*此处好像强制使用了异步方式,而不是依照this.options.asynchronous的值
*/
this.transport.open(this.options.method,url,true);
/**
*这里提供了XmlHttp传输过程中每个步骤的回调函数
*/
if(this.options.asynchronous){
this.transport.onreadystatechange=this.onStateChange.bind(this);
setTimeout((function(){this.respondToReadyState(1)}).bind(this),10);
}
this.transport.setRequestHeader('X-Requested-With','XMLHttpRequest');
this.transport.setRequestHeader('X-Prototype-Version',Prototype.Version);
if(this.options.method=='post'){
this.transport.setRequestHeader('Connection','close');
this.transport.setRequestHeader('Content-type',
'application/x-www-form-urlencoded');
}
this.transport.send(this.options.method=='post'?
this.options.parameters+'&_=':null);
}catch(e){
}
},
onStateChange:function(){
varreadyState=this.transport.readyState;
/**
*如果不是Loading状态,就调用回调函数
*/
if(readyState!=1)
this.respondToReadyState(this.transport.readyState);
},
/**
*回调函数定义在this.options属性中,比如:
varoption={
onLoaded:function(req){};
}
newAjax.Request(url,option);
*/
respondToReadyState:function(readyState){
varevent=Ajax.Request.Events[readyState];
(this.options['on'+event]||Ajax.emptyFunction)(this.transport);
}
});
/**
*Ajax.Updater用于绑定一个html元素与XmlHttp调用的返回值。类似与buffalo的bind。
*如果options中有insertion(fromdom.js)对象的话,insertion能提供更多的插入控制。
*/
AJAX.Updater=Class.create();
AJAX.Updater.prototype=(newAjax.Base()).extend({
initialize:function(container,url,options){
this.container=$(container);
this.setOptions(options);
if(this.options.asynchronous){
this.onComplete=this.options.onComplete;
this.options.onComplete=this.updateContent.bind(this);
}
this.request=newAjax.Request(url,this.options);
if(!this.options.asynchronous)
this.updateContent();
},
updateContent:function(){
if(this.options.insertion){
newthis.options.insertion(this.container,
this.request.transport.responseText);
}else{
this.container.innerHTML=this.request.transport.responseText;
}
if(this.onComplete){
setTimeout((function(){this.onComplete(this.request)}).bind(this),10);
}
}
});
/**
*针对页面元素对象的工具类,提供一些简单静态方法
*/
varField={
/**
*清除参数引用对象的值
*/
clear:function(){
for(vari=0;i<arguments.length;i++)
$(arguments[i]).value='';
},
/**
*使参数引用对象获取焦点
*/
focus:function(element){
$(element).focus();
},
/**
*判断参数引用对象值是否为空,如为空,返回false,反之true
*/
present:function(){
for(vari=0;i<arguments.length;i++)
if($(arguments[i]).value=='')returnfalse;
returntrue;
},
/**
*使选中参数引用对象
*/
select:function(element){
$(element).select();
},
/**
*使参数引用对象处于可编辑状态
*/
activate:function(element){
$(element).focus();
$(element).select();
}
}