Ext源码分析:Ext.o nReady到底做了什么?

注意:这篇文章我是针对FF来写的,看完这篇文章,你能把IE的也写出来,就说明你入门了:)
代码下面有下载, 无 需引入任何js

首先,我们来看下面的代码片段

  1. Crab = {};
  2. Crab.util = {};//把Namespace模拟出来
  3.  
  4. Crab.EventManager = function(){//仔细看看这个单例模式,以后调用Crab.EventManager其实得到的就是pub对象
  5.     var pub = {
  6.         onDocumentReady : function(fn){…};
  7.     };
  8.     return pub;
  9. }();
  10.  
  11. Crab.onReady = Crab.EventManager.onDocumentReady;//命名空间的转换
  12.  
  13. Crab.onReady(function(){
  14.     alert(‘hello crabone.com‘);
  15. });

上面是一个大的架子,我们往里面分析进去。Crab.onReady里的方法其实就是被传入onDocumentReady了。

  1. onDocumentReady : function(fn){
  2.     if(docReadyState){ // 如果已经调用过Crab.onReady()
  3.         docReadyEvent.addListener(fn);
  4.         docReadyEvent.fire();
  5.         docReadyEvent.clearListeners();
  6.         return;
  7.     }
  8.     if(!docReadyEvent){//如果docReadyEvent还没有创建,我们来初始化
  9.         initDocReady();
  10.     }
  11.     docReadyEvent.addListener(fn);
  12. }

看完上面的注释,再仔细分析后,可以看出docReadyEvent是一个重要的家伙,可以说,onReady里的方法就是这家伙通过fire()来执行的。那么我们来看看,创建这个家伙的initDocReady做了些什么?

  1. var initDocReady = function(){
  2.  
  3.     docReadyEvent = new Crab.util.Event();//原来这家伙是它
  4.  
  5.     document.addEventListener(“DOMContentLoaded”, fireDocReady, false);//委托document,当页面载入完成的时候,调用fireDocReady
  6.  
  7. };

看到这里,我们来整理下思路,首先,第一次调用onDocumentReady 的时候 ,做了这么三件事
1、docReadyEvent = new Crab.util.Event();
2、document.addEventListener(”DOMContentLoaded”, fireDocReady, false);
3、docReadyEvent.addListener(fn);
特别是第二件事情,我们要注意,仅仅是委托了而已,并没有执行fireDocReady

OK,做完上面三件事情后,FF又往下面执行下去了,读出所有的DOM后,开始调用fireDocReady了!!
我们来看看fireDocReady干了些什么?

  1. var fireDocReady = function(){
  2.     if(!docReadyState){
  3.         docReadyState = true;//设置为已经调用了onReady了
  4.        
  5.         document.removeEventListener(“DOMContentLoaded”, fireDocReady, false);//将先前的委托取消
  6.        
  7.         if(docReadyEvent){
  8.             docReadyEvent.fire();//fire了
  9.             docReadyEvent.clearListeners();//清空!
  10.         }
  11.     }
  12. };

可见,最终还是由docReadyEvent这家伙在执行我们的方法。

再回顾下,onDocumentReady 里面,当已经执行过onReady的时候,做了些什么?
docReadyEvent.addListener(fn);
docReadyEvent.fire();
docReadyEvent.clearListeners();
可见,还是它。下面我贴出,docReadyEvent的代码片段,记住,它是docReadyEvent = new Crab.util.Event();代码有点长,耐心看下去

  1. (function(){
  2.     Crab.util.Event = function(obj){
  3.         this.listeners = [];
  4.     };
  5.  
  6.     Crab.util.Event.prototype = {
  7.         addListener : function(fn){
  8.  
  9.             if(!this.isListening(fn)){
  10.                 var l = this.createListener(fn);
  11.                 if(!this.firing){
  12.                     this.listeners.push(l);
  13.                 }else{ // if we are currently firing this event, don’t disturb the listener loop
  14.                     this.listeners = this.listeners.slice(0);
  15.                     this.listeners.push(l);
  16.                 }
  17.             }
  18.         },
  19.  
  20.         createListener : function(fn){
  21.             var l = {
  22.                 fn:fn,
  23.                 fireFn:fn
  24.             };
  25.             return l;
  26.         },
  27.  
  28.         findListener : function(fn){
  29.             var ls = this.listeners;
  30.             for(var i = 0, len = ls.length; i < len; i++){
  31.                 var l = ls[i];
  32.                 if(l.fn == fn){
  33.                     return i;
  34.                 }
  35.             }
  36.             return -1;
  37.         },
  38.  
  39.         isListening : function(fn){
  40.             return this.findListener(fn) != -1;
  41.         },
  42.  
  43.         clearListeners : function(){
  44.             this.listeners = [];
  45.         },
  46.  
  47.         fire : function(){
  48.             var ls = this.listeners, len = ls.length;
  49.             if(len > 0){
  50.                 this.firing = true;
  51.                 var args = Array.prototype.slice.call(arguments, 0);
  52.                 for(var i = 0; i < len; i++){
  53.                     var l = ls[i];
  54.                     if(l.fireFn.apply(window, arguments) === false){
  55.                         this.firing = false;
  56.                         return false;
  57.                     }
  58.                 }
  59.                 this.firing = false;
  60.             }
  61.             return true;
  62.         }
  63.     };
  64. })();


网易邮箱,中国第一大电子邮件服务商

发表评论