一? 内存与性能的关系
在 JavaScript 中,页面中事件处理程序的数量与页面整体性能直接相关
- 每个函数都是对象,都占用内存空间,对象越多,性能越差
- 为指定事件处理 程序所需访问 DOM 的次数会先期造成整个页面交互的延迟
二 事件委托
定义:事件委托利用事件冒泡,可以只使用一个事件处理程序来管理一种类型的事件
- 使用事件委托,只要给所有元素共同的祖先节点添加一个事件处理程序, 就可以解决问题
// click 事件冒泡到 document。这意味着可以为整个页面指定一个 onclick 事件处理程序,而不用为每个可点击元素分别指定事件处理程序。比如有以下 HTML:
<ul id="myLinks">
<li id="goSomewhere">Go somewhere</li>
<li id="doSomething">Do something</li>
<li id="sayHi">Say hi</li>
</ul>
let list = document.getElementById("myLinks");
list.addEventListener("click", (event) => {
let target = event.target;
switch(target.id) {
case "doSomething":
document.title = "I changed the document's title";
break;
case "goSomewhere":
location.href = "http:// www.wrox.com";
break;
case "sayHi":
console.log("hi");
break;
}
});
// 这里只给<ul id="myLinks">元素添加了一个 onclick 事件处理程序。因为所有列表项都是这个元素的后代,所以它们的事件会向上冒泡,最终都会由这个函数来处理。
// 但事件目标是每个被点击的列表项,只要检查 event 对象的 id 属性就可以确定,然后再执行相应的操作即可。
// 相对于前面不使用事件委托的代码,这里的代码不会导致先期延迟,因为只访问了一个 DOM 元素和添加了一个事件处理程序。结果对用户来说没有区别,但这种方式占用内存更少。所有使用按钮的事件(大多数鼠标事件和键盘事件)都适用于这个解决方案。
// 只要可行,就应该考虑只给 document 添加一个事件处理程序,通过它处理页面中所有某种类型的事件
优点:
- document 对象随时可用,任何时候都可以给它添加事件处理程序(不用等待DOMContentLoaded 或 load 事件)。这意味着只要页面渲染出可点击的元素,就可以无延迟地起作用
- 节省花在设置页面事件处理程序上的时间。只指定一个事件处理程序既可以节省 DOM 引用,也可以节省时间
- 减少整个页面所需的内存,提升整体性能。 最适合使用事件委托的事件包括:click、mousedown、mouseup、keydown 和 keypress。 mouseover 和 mouseout 事件冒泡,但很难适当处理,且经常需要计算元素位置(因为 mouseout 会在光标从一个元素移动到它的一个后代节点以及移出元素之外时触发)
三 删除事件处理程序
背景:很多 Web 应用性能不佳都是由于无用的事件处理程序长驻内存导致的
原因:
原因分析:
原因一:删除带有事件处理程序的元素
造成的场景:
- 通过真正的 DOM 方法 removeChild()或 replaceChild()删除节点
- 使用innerHTML 整体替换页面的某一部分,被 innerHTML 删除的元素上如果有事件处理程序,就不会被垃圾收集程序正常清理
<div id="myDiv">
<input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
let btn = document.getElementById("myBtn");
btn.onclick = function() {
// 执行操作
document.getElementById("myDiv").innerHTML = "Processing...";
// 不好!
};
</script>
处理方式:
知道某个元素会被删除,那么最好在删除它之前手工删除它的事件处理程序
<div id="myDiv">
<input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
let btn = document.getElementById("myBtn");
btn.onclick = function() {
// 执行操作
btn.onclick = null; // 删除事件处理程序
document.getElementById("myDiv").innerHTML = "Processing...";
};
</script>
原因二:页面卸载
原因分析:如果在页面卸载后事件处理程序没有被清理,则它们仍然会残留在内存中。之后,浏览器每次加载和卸载页面(比如通过前进、后退或刷新),内存中残留对象的数量都会增加,这是因为事件处理程序不会被回收
处理方式:
最好在 onunload 事件处理程序中趁页面尚未卸载先删除所有事件处理程序。这时候也能体现使用事件委托的优势,因为事件处理程序很少,所以很容易记住要删除哪些。关于卸载页面时的清理,可以记住一点:onload 事件处理程序中做了什么,最好在 onunload 事件处理程序中恢复
|