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

本文详细解析了Crab框架中onReady的实现机制,包括单例模式、事件委托、DOM加载事件监听等核心概念。通过实例代码分析,展示了如何利用JavaScript模拟命名空间、事件管理器及文档就绪事件的处理过程。
摘要由CSDN通过智能技术生成
 

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

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

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

上面是一个大的架子,我们往里面分析进去。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.     docReadyEvent = new Crab.util.Event();//原来这家伙是它
  3.     document.addEventListener("DOMContentLoaded", fireDocReady, false);//委托document,当页面载入完成的时候,调用fireDocReady
  4. };

看到这里,我们来整理下思路,首先,第一次调用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.     Crab.util.Event.prototype = {
  6.         addListener : function(fn){
  7.             if(!this.isListening(fn)){
  8.                 var l = this.createListener(fn);
  9.                 if(!this.firing){
  10.                     this.listeners.push(l);
  11.                 }else{ // if we are currently firing this event, don't disturb the listener loop
  12.                     this.listeners = this.listeners.slice(0);
  13.                     this.listeners.push(l);
  14.                 }
  15.             }
  16.         },
  17.         createListener : function(fn){
  18.             var l = {
  19.                 fn:fn,
  20.                 fireFn:fn
  21.             };
  22.             return l;
  23.         },
  24.         findListener : function(fn){
  25.             var ls = this.listeners;
  26.             for(var i = 0, len = ls.length; i < len; i++){
  27.                 var l = ls[i];
  28.                 if(l.fn == fn){
  29.                     return i;
  30.                 }
  31.             }
  32.             return -1;
  33.         },
  34.         isListening : function(fn){
  35.             return this.findListener(fn) != -1;
  36.         },
  37.         clearListeners : function(){
  38.             this.listeners = [];
  39.         },
  40.         fire : function(){
  41.             var ls = this.listeners, len = ls.length;
  42.             if(len > 0){
  43.                 this.firing = true;
  44.                 var args = Array.prototype.slice.call(arguments, 0);
  45.                 for(var i = 0; i < len; i++){
  46.                     var l = ls[i];
  47.                     if(l.fireFn.apply(window, arguments) === false){
  48.                         this.firing = false;
  49.                         return false;
  50.                     }
  51.                 }
  52.                 this.firing = false;
  53.             }
  54.             return true;
  55.         }
  56.     };
  57. })();

上面的代码我已经简单化了,看起来应该不会太累,当然如果您觉得吃不消了,应该去补补javascript的基础了。回到正题,Event其实是一个方法容器,并不应该翻译成监听器容器,因为在这里它根本就没有去监听事件,而是保存很多方法而已,那么fire()的功能就是去循环这个容器,将里面的方法一一执行。这里就引出一个问题了,我们传进去的方法是如何被执行的?要知道
Ext.onReady(function(){});
里面的可是匿名方法啊,不能通过 方法名() 这样的方法来执行,那么怎么办?
通过方法委托来执行! l.fireFn.apply(window, arguments) 将fireFn委托给window执行,看到这里别迷惑,要知道,我们写如下方法
function aa(){}
aa();
其实aa();就应该等于window.aa();大家应该记得window.alert()=alert()吧?呵呵

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值