原创作者: damoqiongqiu   阅读:2740次   评论:3条   更新时间:2011-05-26    

  

   

 

     还好,这段代码也不多。先不要管其它的东西,我们先来看这段代码本身的流程,然后再来看各个细节是怎么实现的:

 

如果 docReadyEvent 这个对象为空,就去执行 initDocReady() 这个方法;

 

接下来再判断,如果 docReadyState 或者 Ext.isReady 这两个标志位为真,立即执行传递进来的 function ,或者按照 options 这个参数里面的 delay 属性的值延迟执行传进来的 function

 

备注: defer 方法是 Ext 对原生 Function 类的扩展,每个函数都有这个方法,作用是延迟一定的时间再执行。详细描述请翻阅我的 blog :“《仔仔细细分析 Ext

Ext Function 类的扩展” )。

 

         否则(文档还没初始化好),把我们传递进来的函数作为一个监听器,添加到 docReadyEvent 这个事件里面去。

 

结合我们自己做的那个小例子,到这个地方我们可以很敏感地推测出来,在添加监听器这个地方 Ext 肯定把我们传递进来的方法放到某个数组里面去了,然后等文档加载完成的时候再来调用这些方法,就像我们自己做的那样。

 

         只是,这里的数组它不是一个简单的数组,而是作为 docReadyEvent 的一个属性出现的。来看看 docReadyEvent 到底是谁。沿着源码向上查找,我们发现了 docReadyEvent 的身影:



 


原来它是 Ext.util.Event 的一个实例,好,我们把 Ext.util.Event 揪出来(它是在 util 包下的 Observable.js 这个文件里面定义的):



 

 

    哈哈,原来它是如此简单的一个对象!我们的推断没错,它里面果然用了一个数组 (listeners) 来放我们的 function !至于它的 addListener() 方法和最终怎么去调用我们传递进来的方法的(看它的 fire() 方法),这里不多罗嗦了,代码也很简单,原理就和我们自己制作的那个小例子相同。

        

         好,到这里,感觉还没有什么太特别的地方。既然 Ext 在文档没有加载完的时候把我们的 function 存起来了,那么,在文档加载完之后它又是在哪里调用的呢?这个调用肯定与上面我们略过的 initDocReady() 这个方法有关,看代码:

//检测文档是否加载完成
    var initDocReady = function(){
        docReadyEvent = new Ext.util.Event();

        if(Ext.isReady){
            return;
        }

        // no matter what, make sure it fires on load
        //确保文档加载完成事件能触发(这样fireDocReady()方法才能执行,从能设置文档状态并清除轮询定时器)
        E.on(window, 'load', fireDocReady);
		
        if(Ext.isGecko || Ext.isOpera) {//Gecko和Opera下的文档加载完成检测方法
            document.addEventListener('DOMContentLoaded', fireDocReady, false);
        }
        else if(Ext.isIE){//IE下的文档加载完成检测方法
            docReadyProcId = setInterval(function(){
                try{
                	//这一句一直抛出异常,直到文档加载完成
                    Ext.isReady || (document.documentElement.doScroll('left'));
                }catch(e){
                    return;
                }
                fireDocReady();  //没有异常抛出,则说明文档已经加载完成,手工fire文档加载完成事件
            }, 5);

			document.onreadystatechange = function(){
				if(document.readyState == 'complete'){
					document.onreadystatechange = null;
					fireDocReady();
				}
            };
        }
        else if(Ext.isSafari){//Safari下的文档加载完成检测方法
            docReadyProcId = setInterval(function(){
                var rs = document.readyState;
                if(rs == 'complete') {
                    fireDocReady();
                 }
            }, 10);
        }
};
 

 

这段代码看似很长,实际上做的事情并不多。为了兼容浏览器,里面做了三个判断,我们来看最好懂的 IE 的情况,其它的都可以类比。

 



 总体来看,它就两小段代码,第一段实际上是个定时器,每隔 5 毫秒就去尝试让 document 的滚动一下,如果能滚动(也就是 document 已经初始化完成了)就执行 fireDocReady 。从名称可以看出, fireDocReady 方法是在触发“文档准备好了”这个事件。可以推断,在这个方法里面一定会调用我们传进来的那些(被缓存)的方法。



 看到第 119 行的 docReadyEvent.fire() 方法了吧,它先做了一些扫尾的工作,设置 Ext.isReady 标志位啦、清除定时器啦等等,然后就去调用了我们的 function

        

         好了,解释到这个地方,其它的代码就无需多言了,应该都能轻松地看明白了。这里也提出一个小小的疑问:在 initDocReady() 这个方法里面,是用一个定时器来不断“重复错误”的方式来检测 document 是不是已经加载完成的。我曾经记得某本“经典”的 JAVA C++ 书籍里面告诫程序员,不要企图使用“抛异常”的方式来做常规的、正常的流程判断。不知道有没有人研究过这块“尝试错误”的代码是不是存在什么样的问题(效率或内存)?如果你知道的话,请一定记得告诉我一声(群 88403922 )。

评论 共 3 条 请登录后发表评论
3 楼 火大了 2011-05-08 22:26
2 楼 maidou80 2011-04-26 16:20
刚试图加入你的群  提示拒绝加入  兄弟拉一把  谢谢
1 楼 TheMatrix 2009-07-12 13:48
无论如何,虽然看不懂,但是看了就要顶,作为一个看官,这是基本的素质。

发表评论

您还没有登录,请您登录后再发表评论

文章信息

Global site tag (gtag.js) - Google Analytics