- 浏览: 397036 次
- 性别:
- 来自: 北京
文章分类
最新评论
IE的 JScript 存在内存泄露的bug 想必大家都清楚或者有耳闻了。这是由于IE的内存回收管理器的一个设计错误导致的。当我们编写脚本的时候创建了交叉引用,例如如下代码: window.onload = function () {
var x = document.getElementsByTagName(’H3’);
for (var i=0;i<x.length;i++)
{
x[i].onclick = openClose;
x[i].relatedElement = x[i].nextSibling; // simplified situation
x[i].relatedElement.relatedElement = x[i];
}
}或者在函数中使用脚本语言最常见的闭句Closures的时候,IE都无法回收内存。而闭句在给DOM对象注册事件处理器(event handler)的时候最为常用。Novemberborn提供了一些example可以让你运行并切实感受到这个bug。
我最喜爱的QuirkMode 去年初意识到这个bug存在巨大隐患,觉得有必要呼吁广大web开发者关注并竭力避免这个问题,于是举办了一个慈善邀请赛,鼓励大家提交各自addEvent/removeEvent 方案。并终于在去年10月下旬宣布了他们认为的胜利者:John Resig,让 John赢得胜利的代码如下:
function addEvent( obj, type, fn ) {
if ( obj.attachEvent ) {
obj[’e’+type+fn] = fn;
obj[type+fn] = function(){obj[’e’+type+fn]( window.event );}
obj.attachEvent( ’on’+type, obj[type+fn] );
} else
obj.addEventListener( type, fn, false );
}
function removeEvent( obj, type, fn ) {
if ( obj.detachEvent ) {
obj.detachEvent( ’on’+type, obj[type+fn] );
obj[type+fn] = null;
} else
obj.removeEventListener( type, fn, false );
}
QuirkMode 对选择John为胜利者的解释概括来说就是以上代码最简洁有效,在避免内存问题的同时还巧妙的保证了this关键字在ie的attachEvent中能正常工作。缺点当然还是存在:
不支持 Netscape 4 和 Explorer 5 Mac。(有可能国内的程序员会嗤之以鼻,但国外很强调广泛的兼容性)
在 removeEvent 中遗漏了remove obj["e"+type+fn]。
总之不管怎么说,简单取胜。
结果一出,众多参赛与评论者不服气,很快又挑出了John的代码的几处毛病:
addEvent中本身就使用了闭句,所以没有根本解决IE内存泄露的问题。
没有解决同类型的事件可能被重复注册而被IE重复执行的问题。
几个高手于是提出了改进性的方案: /*
Original idea by John Resig
Tweaked by Scott Andrew LePera, Dean Edwards and Peter-Paul Koch
Fixed for IE by Tino Zijdel (crisp)
Note that in IE this will cause memory leaks and still doesn’t quite function the same as in browsers that do support the W3C event model:
- event execution order is not the same (LIFO in IE against FIFO)
- functions attached to the same event on the same element multiple times will also get executed multiple times in IE
*/
function addEvent( obj, type, fn ) {
if (obj.addEventListener)
obj.addEventListener( type, fn, false );
else if (obj.attachEvent) {
obj["e"+type+fn] = fn;
obj.attachEvent( "on"+type, function() { obj["e"+type+fn](); } );
}
}
function removeEvent( obj, type, fn ) {
if (obj.removeEventListener)
obj.removeEventListener( type, fn, false );
else if (obj.detachEvent) {
obj.detachEvent( "on"+type, obj["e"+type+fn] );
obj["e"+type+fn] = null;
}
}
很明显,虽然修正了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的最终版本是最全面的解决方案。不过就我个人意见,感觉有些吹毛求疵了。尽量使用浏览器本身的实现和保持简单是我一贯坚持的主张。但洋人这种严谨的态度,还是让我深深敬佩。
var x = document.getElementsByTagName(’H3’);
for (var i=0;i<x.length;i++)
{
x[i].onclick = openClose;
x[i].relatedElement = x[i].nextSibling; // simplified situation
x[i].relatedElement.relatedElement = x[i];
}
}或者在函数中使用脚本语言最常见的闭句Closures的时候,IE都无法回收内存。而闭句在给DOM对象注册事件处理器(event handler)的时候最为常用。Novemberborn提供了一些example可以让你运行并切实感受到这个bug。
我最喜爱的QuirkMode 去年初意识到这个bug存在巨大隐患,觉得有必要呼吁广大web开发者关注并竭力避免这个问题,于是举办了一个慈善邀请赛,鼓励大家提交各自addEvent/removeEvent 方案。并终于在去年10月下旬宣布了他们认为的胜利者:John Resig,让 John赢得胜利的代码如下:
function addEvent( obj, type, fn ) {
if ( obj.attachEvent ) {
obj[’e’+type+fn] = fn;
obj[type+fn] = function(){obj[’e’+type+fn]( window.event );}
obj.attachEvent( ’on’+type, obj[type+fn] );
} else
obj.addEventListener( type, fn, false );
}
function removeEvent( obj, type, fn ) {
if ( obj.detachEvent ) {
obj.detachEvent( ’on’+type, obj[type+fn] );
obj[type+fn] = null;
} else
obj.removeEventListener( type, fn, false );
}
QuirkMode 对选择John为胜利者的解释概括来说就是以上代码最简洁有效,在避免内存问题的同时还巧妙的保证了this关键字在ie的attachEvent中能正常工作。缺点当然还是存在:
不支持 Netscape 4 和 Explorer 5 Mac。(有可能国内的程序员会嗤之以鼻,但国外很强调广泛的兼容性)
在 removeEvent 中遗漏了remove obj["e"+type+fn]。
总之不管怎么说,简单取胜。
结果一出,众多参赛与评论者不服气,很快又挑出了John的代码的几处毛病:
addEvent中本身就使用了闭句,所以没有根本解决IE内存泄露的问题。
没有解决同类型的事件可能被重复注册而被IE重复执行的问题。
几个高手于是提出了改进性的方案: /*
Original idea by John Resig
Tweaked by Scott Andrew LePera, Dean Edwards and Peter-Paul Koch
Fixed for IE by Tino Zijdel (crisp)
Note that in IE this will cause memory leaks and still doesn’t quite function the same as in browsers that do support the W3C event model:
- event execution order is not the same (LIFO in IE against FIFO)
- functions attached to the same event on the same element multiple times will also get executed multiple times in IE
*/
function addEvent( obj, type, fn ) {
if (obj.addEventListener)
obj.addEventListener( type, fn, false );
else if (obj.attachEvent) {
obj["e"+type+fn] = fn;
obj.attachEvent( "on"+type, function() { obj["e"+type+fn](); } );
}
}
function removeEvent( obj, type, fn ) {
if (obj.removeEventListener)
obj.removeEventListener( type, fn, false );
else if (obj.detachEvent) {
obj.detachEvent( "on"+type, obj["e"+type+fn] );
obj["e"+type+fn] = null;
}
}
很明显,虽然修正了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的最终版本是最全面的解决方案。不过就我个人意见,感觉有些吹毛求疵了。尽量使用浏览器本身的实现和保持简单是我一贯坚持的主张。但洋人这种严谨的态度,还是让我深深敬佩。
发表评论
-
[转]js获取滚动条位置:绝对有效(经验之谈)
2010-02-26 13:33 7155获取坐标: IE (event.x event.y) 获取滚 ... -
Javascript 最简单检测网速的方法和应用
2010-01-18 10:47 2012Javascript 最简单检测网速的方法和应用。网速很慢,但 ... -
js自定义对象
2009-11-18 14:49 34723一,概述 在Java语言中,我们可以定义自己的类,并根据这 ... -
IE6,IE7和FireFox兼容处理(持续发现中)
2009-11-18 13:30 22801. event.srcElement 属性: IE下可以使用 ... -
Javascript的IE和Firefox兼容性汇编
2009-11-18 13:29 834微软关于IE、Firefox、Opera和Safari的Jav ... -
FF IE 兼容动态增加onclick事件
2009-11-17 13:36 1971<!DOCTYPE HTML PUBLIC &quo ... -
javascript十个最常用的自定义函数
2009-11-17 11:21 923如果不使用类库或者没有自己的类库,储备一些常用函数总是有好处的 ... -
JavaScript通过attachEvent和detachEvent方法处理带参数的函数
2009-11-17 10:59 21011 <!DOCTYPE html PUBLIC &quo ... -
用javascript编写优美的代码
2009-11-17 10:53 1104一、简化代码 采用更为简短的写法,不仅可以减少输入的字符数, ... -
如何使用Javascript格式化日期显示
2009-09-02 15:36 2309我们都知道在Java和PHP语 ... -
Javascript操作Select和Option
2009-08-25 10:54 1206注意:Option中的O是要大写的,不然语法报错 1 ... -
javascript下的时间函数详解
2009-08-21 13:18 1070Date (对象) Date 对象能够使你获得相对于国际标准 ... -
JS网页高度相关
2009-08-18 11:43 1649<SCRIPT LANGUAGE=" ... -
JS 中面向对象的5种写法
2009-08-12 12:16 1450http://www.iteye.com/topic/4344 ... -
用javasript判断一个图片的宽度,如果宽度小于一个数值,就以正常显示,大于就设置宽度width属性
2009-08-12 10:47 1291<script language=" ... -
js替换br 为换行 textarea
2009-07-30 15:41 4630我们有正则表达式来替换内容中的br让它在能textarea中也 ... -
js操作html增加删除tr/td
2009-07-17 13:33 5603总结:可以通过parentElement定位父级元素,有待验证 ... -
判断页面属于哪个iframe
2009-07-16 13:53 1360在相应iframe的页面中使用window.name 可以得到 ... -
最基础的JS添加样式实例
2009-07-15 15:32 1697<style> .test { borde ... -
Js动态添加样式
2009-07-15 15:31 4014<script type="text/ja ...
相关推荐
浏览器事件动态注册和取消是Web开发中的重要概念,它涉及到JavaScript和DOM(Document Object Model)交互的核心机制。在本文中,我们将深入探讨如何动态地添加和移除事件监听器,以及这对网页性能和用户体验的影响...
1. 注册和注销事件监听器:允许在运行时动态添加或移除事件监听。 2. 广播事件:将事件发送给所有注册的监听器,而不仅仅是单个目标。 3. 分发事件:根据事件类型将事件分发到不同的处理函数。 4. 错误处理:提供...
本文探讨了基于Asp.Net的Web注销功能的实现,主要涉及到Session信息的分析和清理、JavaScript的应用、onunload事件的使用等技术。通过在Default.aspx页面验证用户身份成功后,将用户引导到main.htm页面,该页面包含...
JSP是Java的一种动态网页技术,可以将HTML、CSS、JavaScript与Java代码混合编写。在这个实例中,JSP可能用于创建视图,比如login.jsp、register.jsp等,它们可以包含HTML元素和JSP标签来处理请求和响应。JSP中的...
5. **事件处理和DOM操作**:在JavaScript中,我们需要熟练掌握事件监听(如`addEventListener`)和DOM操作(如`createElement`, `appendChild`, `removeChild`等)来实现动态生成和删除Tab。此外,理解Bootstrap的...
JavaScript(简称JS)是一种轻量级的脚本语言,广泛应用于网页和网络应用开发,而动态生成二维码是JS在移动互联网时代的一个实用功能。通过这个功能,开发者可以将各种信息编码为二维码,用户扫描后可以直接在手机上...
在Java Web开发中,JSP(JavaServer Pages)和JDBC(Java Database Connectivity)是两个核心组件,用于构建动态网页和与数据库交互。本项目是一个基于JSP和JDBC实现的登录系统,包含了登录、成功登录、失败登录以及...
这个公式是LotusScript的写法,与JavaScript代码类似,也是构建一个包含注销请求的URL,并指定了注销后要打开的表单('f_index'),同时用`OpenForm`动作来加载这个表单。 这些方法都是在前端通过JavaScript或...
首先,我们需要确保已经安装了必要的开发环境,包括Node.js、npm(Node包管理器)、Ionic CLI和Angular CLI。安装完毕后,我们可以通过`ionic start`命令创建一个新的项目,指定项目名为"login-logout",并选择...
在"例1-12(js鼠标事件响应)"这个文件中,很可能是示例代码,演示了如何使用JavaScript来处理鼠标的移动、点击等事件。JavaScript是一种常用的客户端脚本语言,它可以轻松地添加动态效果和交互性到HTML页面。例如,...
总结起来,实现Cookie自动填充和注销功能涉及对Cookie的理解、JavaScript的事件处理和存储操作。这个小案例提供了一个实践的平台,有助于巩固这些理论知识,提高实际开发能力。在实际项目中,还需要考虑安全性、隐私...
"js 手动封装的各种效果"这个主题涵盖了如何使用纯JavaScript编写各种功能模块,这些模块可以被重复利用,而不需要依赖额外的库或框架。让我们深入探讨一下其中可能包含的知识点: 1. **函数封装**:在JavaScript中...
更新时间:2015-08-31 18:00 Discourse已将站点设置添加到核心以启用“注销”菜单项。 只需在管理员设置中搜索“注销”即可启用它。话语注销菜单项该插件将注销选项添加到Discourse的用户菜单中。安装按照使用作为...
`activate()`和`deactivate()`方法用于激活和注销控件,这涉及到动态地绑定和解除绑定事件处理器。当控件被激活时,它的处理器也会被激活,反之亦然。 OpenLayers提供了多种预定义的控件,如PanZoomBar(平移和缩放...
JavaScript,也被称为JS,是一种广泛应用于网页和网络应用的编程语言,尤其在前端开发中扮演着核心角色。作为一门轻量级的解释型语言,JavaScript主要用于实现客户端的交互性、动态效果以及数据验证等任务。它与HTML...
本例程专注于使用JavaScript库d3.js来实现这一功能。 d3.js(Data-Driven Documents)是一个强大的JavaScript库,专为创建数据驱动的Web图形而设计。它结合了SVG、CSS和HTML等现代Web标准,提供了丰富的工具和函数...
4. 动画与交互:为了增加交互性,可以添加鼠标拖动旋转、缩放和平移功能,以及点击或悬停事件,以高亮显示特定区域。 5. 渲染循环:通过requestAnimationFrame函数实现渲染循环,每次更新相机位置和角度,以实现...
**Node.js - AgeraBus:基于谷歌Agera的Android事件总线** 在Android应用开发中,事件驱动的设计模式已经成为提高代码可维护性和模块化的重要手段。EventBus是一种广泛使用的发布/订阅事件总线库,它使得组件之间的...
9. **事件冒泡与事件捕获** - 事件冒泡:事件从最深的节点开始,逐级向上层节点传播事件。 - 事件捕获:事件从最外层节点开始,逐级向下层节点传播事件。 - `event.stopPropagation()`阻止事件冒泡,`event....
JavaScript,也被称为JS,是一种广泛应用于网页和网络应用的编程语言,主要负责实现客户端的交互效果和动态功能。作为Web开发三大核心技术之一(HTML、CSS、JavaScript),它在现代互联网中扮演着至关重要的角色。...