时间:2021-05-18
当我们编写脚本的时候创建了交叉引用,例如如下代码:
或者在函数中使用脚本语言最常见的闭句Closures的时候,IE都无法回收内存。而闭句在给DOM对象注册事件处理器(event handler)的时候最为常用。Novemberborn提供了一些example可以让你运行并切实感受到这个bug。
我最喜爱的QuirkMode 去年初意识到这个bug存在巨大隐患,觉得有必要呼吁广大web开发者关注并竭力避免这个问题,于是举办了一个慈善邀请赛,鼓励大家提交各自 addEvent/removeEvent 方案。并终于在去年10月下旬宣布了他们认为的胜利者:John Resig,让John赢得胜利的代码如下:
QuirkMode 对选择John为胜利者的解释概括来说就是以上代码最简洁有效,在避免内存问题的同时还巧妙的保证了this关键字在ie的attachEvent中能正常工作。缺点当然还是存在:
不支持 Netscape 4 和 Explorer 5 Mac。(有可能国内的程序员会嗤之以鼻,但国外很强调广泛的兼容性)
在 removeEvent 中遗漏了remove obj["e"+type+fn]。
总之不管怎么说,简单取胜。
结果一出,众多参赛与评论者不服气,很快又挑出了John的代码的几处毛病:
addEvent中本身就使用了闭句,所以没有根本解决IE内存泄露的问题。
没有解决同类型的事件可能被重复注册而被IE重复执行的问题。
几个高手于是提出了改进性的方案:
很明显,虽然修正了John代码的一些不足。但内存泄露依然存在,部分浏览器依然不支持,还是无法避免ie重复注册。另外根据注释:当在同一个对象上注册多个事件处理器的时候,IE与其他浏览器的执行顺序是不同的,这又是一个隐患。
几天之后,一个被认为最严谨的方案由Dean Edwards 提出。Dean他的方案与众不同:
不执行对象检测(Object detection)
没有调用 addeventListener/attachEvent 方法
保持this关键字的运行于正确的上下文环境
正确传递 event 对象参数
完全跨浏览器至此(包括IE4和NS4)
不存在内存泄露
Dean的代码如下:
// written by Dean Edwards, 2005 // http://dean.edwards.name/function ;addEvent(element, type, handler) { // assign each event handler a unique ID // 为事件处理函数设定一个唯一值 if (!handler.$$guid) handler.$$guid = addEvent.guid++;// create a hash table of event types for the element if (!element.events) element.events = {};// create a hash table of event handlers for each element/event pair var handlers = element.events[type];if (!handlers) { handlers = element.events[type] = {}; // store the existing event handler (if there is one) // 如果对象已经注册有事件处理,那么要保留下来,并保存为第一个 if (element["on" + type]) { handlers[0] = element["on" + type]; }}// store the event handler in the hash table handlers[handler.$$guid] = handler;// assign a global event handler to do all the work // 指派一个全局函数做统一的事件处理,同时避免了反复注册 element["on" + type] = handleEvent;};// a counter used to create unique IDs addEvent.guid = 1;function removeEvent(element, type, handler) { // delete the event handler from the hash table if (element.events && element.events[type]) { delete element.events[type][handler.$$guid]; }};function handleEvent(event) { // grab the event object (IE uses a global event object) event = event || window.event; // get a reference to the hash table of event handlers // 这里的 this 随 handlerEvent function 被触发的source element 变化而变化 var handlers = this.events[event.type]; // execute each event handler for (var i in handlers) { //这样写才能保证注册的事件处理函数中的 this 得到正确的引用,直接handlers[i]()是不行的 this.$$handleEvent = handlers[i]; this.$$handleEvent(event); }};这段代码相比之前就大了不少了,不过确实很精妙。可是这段代码却引入了其他的问题,比如无法处理事件处理函数的返回值,for..in循环可能因为 (Object.prototype)的错误应用而中断等等...很快Dean推出一个"updated version"。
要做到最好真的好辛苦。
目前似乎Dean的最终版本是最全面的解决方案。不过就我个人意见,感觉有些吹毛求疵了。尽量使用浏览器本身的实现和保持简单是我一贯坚持的主张。但洋人这种严谨的态度,还是让我深深敬佩。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
本文实例讲述了JS事件绑定的常用方式。分享给大家供大家参考,具体如下:常用的事件绑定的几种方式有三种:直接在dom元素上进行绑定。用on绑定。用addEvent
event究竟有多么复杂?可见前辈的6年前的努力:最佳的addEvent是怎样诞生的,后起之秀jQuery也付出了一千六百多行血汗代码(v1.5.1)搞定了6年
事件绑定分为两种:一种是传统事件绑定(内联模型,脚本模型),一种是现代事件绑定(DOM2级模型)。现代事件绑定在传统绑定上提供了更强大更方便的功能。一传统事件绑
一.事件绑定1.事件绑定函数事件的绑定函数为如下形式:.bind(type[,data],fn)type:类型如click.....data:参数fn:事件执行
事件绑定分为两种:一种是传统事件绑定(内联模型/脚本模型);上一章内容;一种是现代事件绑定(DOM2级模型);现代事件绑定在传统事件绑定基础上提供了更强大的功能