zoukankan      html  css  js  c++  java
  • js便签笔记(5)——Dean Edwards大牛的跨浏览器AddEvent()设计(不知道是不是jQuery事件系统的原型)

    1. 前言:

    在看Aaron的jquery源码解读时候,看到事件系统那块,作者提到了Dean Edwards的添加事件的设计,于是就点进去看了看。首先让我吃惊的是,代码非常少,寥寥几十行,非常简单。于是我就仔细的看了看(如果代码太多,可能就直接不看了)。

    这段代码是Dean Edwards在2005年写的了,那时候还没有jquery。但是它的设计思路确实和jquery的事件系统有些相似,即便是在9年之后的今天。

    于是把这段代码仔细研究,并在此跟大家分享以下。它将帮助你更好的理解jquery的事件系统。

    先把源码粘上,:

     1 function addEvent(element, type, handler) {
     2     // assign each event handler a unique ID
     3     if (!handler.$$guid) handler.$$guid = addEvent.guid++;
     4     // create a hash table of event types for the element
     5     if (!element.events) element.events = {};
     6     // create a hash table of event handlers for each element/event pair
     7     var handlers = element.events[type];
     8     if (!handlers) {
     9         handlers = element.events[type] = {};
    10         // store the existing event handler (if there is one)
    11         if (element["on" + type]) {
    12             handlers[0] = element["on" + type];
    13         }
    14     }
    15     // store the event handler in the hash table
    16     handlers[handler.$$guid] = handler;
    17     // assign a global event handler to do all the work
    18     element["on" + type] = handleEvent;
    19 };
    20 // a counter used to create unique IDs
    21 addEvent.guid = 1;
    22 
    23 function removeEvent(element, type, handler) {
    24     // delete the event handler from the hash table
    25     if (element.events && element.events[type]) {
    26         delete element.events[type][handler.$$guid];
    27     }
    28 };
    29 
    30 function handleEvent(event) {
    31     // grab the event object (IE uses a global event object)
    32     event = event || window.event;
    33     // get a reference to the hash table of event handlers
    34     var handlers = this.events[event.type];
    35     // execute each event handler
    36     for (var i in handlers) {
    37         this.$$handleEvent = handlers[i];
    38         this.$$handleEvent(event);
    39     }
    40 };
    View Code

    2. 该设计的优点:

    Dean Edwards在文章中提到了该设计的几个优点:

    it performs no object detection  不执行对象检测,不理解何意。。

    it does not use the addeventListener/attachEvent methods  不使用addeventListener/attachEvent方法,因为这两个方法分别由不同的浏览器支持,使用时候需要判断。  但是,在Dean Edwards提供的下载代码中,应用到了addeventListener。

    it keeps the correct scope (the this keyword)  保持正确的作用域,即this关键字

    it passes the event object correctly  正确的传递event对象

    it is entirely cross-browser (it will probably work on IE4 and NS4)  保证浏览器兼容性,甚至支持IE4和NetSape4(2005年)

    and from what I can tell it does not leak memory  不会出现内存泄漏

    3. 代码解读:

    3.1 事件添加方法addEvent():

     1         //事件添加方法
     2         function addEvent(element, type, handler) {
     3 
     4             // assign each event handler a unique ID
     5             // 为传入的每个事件初始化一个唯一的id
     6             if (!handler.$$guid) handler.$$guid = addEvent.guid++;   //下文:addEvent.guid = 1;
     7 
     8             // create a hash table of event types for the element
     9             // 给element维护一个events属性,初始化为一个空对象。  
    10             // element.events的结构类似于 { "click": {...}, "dbclick": {...}, "change": {...} }  
    11             // 即element.events是一个对象,其中每个事件类型又会对应一个对象
    12             if (!element.events) element.events = {};
    13 
    14             // create a hash table of event handlers for each element/event pair
    15             // 试图取出element.events中当前事件类型type对应的对象,赋值给handlers
    16             var handlers = element.events[type];
    17             if (!handlers) {
    18                 handlers = element.events[type] = {};
    19                 //如果handlers是undefined,则初始化为空对象
    20 
    21 
    22                 // store the existing event handler (if there is one)
    23                 // 如果这个element已经有了一个方法,例如已经有了onclick方法
    24                 // 就把element的onclick方法赋值给handlers的0元素,此时handlers的结构就是:
    25                 // { 0: function(e){...} }
    26                 // 此时element.events的结构就是: { "click": { 0: function(e){...} },  /*省略其他事件类型*/ } 
    27                 if (element["on" + type]) {
    28                     handlers[0] = element["on" + type];
    29                 }
    30             }
    31             // store the event handler in the hash table
    32             // 把当前的事件handler存放到handlers中,handler.$$guid = addEvent.guid++; addEvent.guid = 1; 肯定是从1开始累加的
    33             // 因此,这是handlers的结构就是 { 0: function(e){...}, 1: function(){}, 2: function(){} 等等... }
    34             handlers[handler.$$guid] = handler;
    35 
    36             // assign a global event handler to do all the work
    37             // 下文定义了一个handleEvent(event)函数
    38             // 将这个函数,绑定到element的type事件上。  说明:在element进行click时,将会触发handleEvent函数,handleEvent函数将会查找element.events,并调用相应的函数。可以把handleEvent称为“主监听函数”
    39             element["on" + type] = handleEvent;
    40         };
    41 
    42         // a counter used to create unique IDs
    43         addEvent.guid = 1;

    以上代码都给出了详细的注释,应该能看明白了。重新梳理以下数据结构,经过addEvent()函数之后,当前的数据结构为:(假如type = 'click')

         element: {
                    onclick: handleEvent(event),   /*下文定义的函数*/
                    events: {
                        click:{
                            0: function(){...},    /*element已有的click事件*/
                            1: function(){...},
                            2: function(){...}
                            /*.......其他事件......*/
                        },
                        change:{
                            /*省略*/
                        },
                        dbclick:{
                            /*省略*/
                        }
                    }
            }

    这样的设计,其实已经具备了jquery事件系统的雏形,包含了两个最主要的特点:

    • element上的所有事件,将保存到element.events属性中,不是直接绑定到element上;
    • handleEvent作为element所有事件的“主监听函数”,有它统一管理element上的所有函数。

    接着往下看:

    3.2 主监听函数handleEvent(event):

     1         //主监听函数
     2         function handleEvent(event) {
     3             // grab the event object (IE uses a global event object)
     4             // 在IE中,event需要通过window.event获取
     5             event = event || window.event;
     6 
     7             // get a reference to the hash table of event handlers
     8             // 根据事件类型在events中获取事件集合(events的数据结构,参考addEvent方法的注释)
     9             var handlers = this.events[event.type];
    10             // 注意!注意!  这里的this不是window,而是element对象,因为上文 element["on" + type] = handleEvent;
    11             // 所以在程序执行时,handleEvent已经作为了element的一个属性,它的作用域是element,即this === element
    12 
    13             // execute each event handler
    14             // 循环执行handlers集合里的所有函数    另外,这里执行事件时传递的event,无论在什么浏览器下,都是正确的
    15             for (var i in handlers) {
    16                 this.$$handleEvent = handlers[i];
    17                 this.$$handleEvent(event);
    18 
    19                 //此处为何要把handlers[i]赋值给this.$$handleEvent,然后在执行呢?
    20                 //而不是直接执行handlers[i](event)?
    21                 //跟内存泄漏有关?
    22                 //我也没看明白,大家自己思考的,知道的可以分享给大家。
    23             }
    24         };

    以上就是主监听函数的实现过程,都做了注释,也不叫好理解,有个问题,已经在代码中有黄色背景标出来了,有了解的,也麻烦分享给大家。

    jquery的主监听函数执行时候实现的比较复杂,但是思路上和这个是一样的。

    3.3 移除函数事件:

    1        //移除函数事件
    2         function removeEvent(element, type, handler) {
    3             // delete the event handler from the hash table
    4             // 循环element.events[type],根据handler的唯一的id,进行delete
    5             if (element.events && element.events[type]) {
    6                 delete element.events[type][handler.$$guid];
    7             }
    8         };

    移除函数比较简单,不多解释。

    4. 总结

    这段代码相对于jquery的事件系统,少了事件的代理,以及模拟的时间冒泡。不考虑代理,当然就简单许多。

    但是它已经点出了jquery事件系统的原型,理解它,能帮助你更好的理解jquery事件系统。

    补充:司徒正美的新书《javascript框架设计》中专门有一节讲解:11.4 Dean Edward的addEvent.js源码分析 260页

  • 相关阅读:
    又见Python<4>:Pandas之DataFrame对象的使用
    又见Python<3>:Pandas之Series对象的使用
    使用tdload工具将本地数据导入到Teradata数据库中
    解决ubuntu系统root用户下Chrome无法启动问题
    又见Python<2>:如何安装第三方库(Windows)
    又见Python<1>:使用Anaconda搭建Python开发环境(Windows7)
    数据仓库原理<4>:联机分析处理(OLAP)
    数据仓库原理<3>:数据仓库与ODS
    配置hibernate,Struts。文件
    jQuery +ajax +json+实现分页
  • 原文地址:https://www.cnblogs.com/wangfupeng1988/p/3659470.html
Copyright © 2011-2022 走看看