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();

}

}

相关推荐