内存与性能 ---性能优化(删除事件处理程序)

1,899 阅读3分钟

本文已参与「[掘力星计划],赢取创作大礼包,挑战创作激励金。

小知识,大挑战!本文正在参与“[程序员必备小知识]创作活动。

前言

上一节提到通过事件委托来提高性能,这一节我们来讲一讲删除事件处理程序。我们知道把事件处理程序指定给元素后,在浏览器代码和负责页面交互的JavaScript代码之间建立联系。这种联系建立的越多,页面性能就越差。除了通过事件委托来限制这种连接外,还可以删除不用的事件处理程序。很多web应用性能不佳都是由于无用的事件处理程序常驻内存导致的。

导致这个问题的原因主要有两个。第一个原因是删除带有事件处理程序的元素,第二个是页面卸载。我们来谈一谈。

处理带事件处理程序的元素

我们通常通过真正的DOM方法removeChild()或者replaceChild()删除节点。最常见的还是使用innerHTML来整体替换页面的某一部分。这时候,被innerHTML删除的元素上如果带有事件处程序,就不会被垃圾收集程序正常清理。例如:

<div id="myDiv">
   <input type="button" value="Click me" id="mybtn">
</div>
<script>
  let btn =document.getElementById('mybtn');
  btn.onclick=function(){
      document.getElementById('myDiv').innerHTML="processing"
}    //不推荐
</script>

这里的按钮在div元素中。单击按钮,会将自己删除并替换为一条消息,以防止双击发生。这是网站上常见的做法。但是问题在于,按钮删除之后仍然保留着一个事件处理程序。在div元素设置innerHTML会完全删除按钮,但事件处理程序仍然挂在按钮上面。某些浏览器,特别是IE8以前的版本(包括IE8),在这里就有问题了。很有可能元素的引用和事件处理程序的引用都会残留在内存中。如果知道某个元素会被删除,那么最好在删除它之前手动删除它的事件处理程序。例如:

<div id="myDiv">
   <input type="button" value="Click me" id="mybtn">
</div>
<script>
  let btn =document.getElementById('mybtn');
  btn.onclick=function(){
      btn.onclick=null;//   手动删除事件处理程序
      document.getElementById('myDiv').innerHTML="processing"
}      //推荐
</script>

在这个重写后的例子中,设置div元素的innerHTML属性之前,按钮的事件处理程序先被删除按钮也可以安全地从 DOM中删掉。

但也要注意,在事件处理程序中删除按钮会阻止事件冒泡。只有事件目标仍然存在于文档中时,事件才会冒泡。

注意:事件委托也有助于解决这种问题。如果提前知道页面某一部分会被使用innerHTML删除,就不要直接给该部分中的元素添加事件处理程序了。把事件处理程序添加到更高层级的节点上同样可以处理该区域的事件。就看大家喜欢用哪一个了。

处理页面卸载问题

首先我给大家介绍一下页面卸载吧,网页前进、后退或者刷新这种情况就叫做页面卸载。

同样,IE8及更早的版本在这种情况下有很多问题,但是几乎所有的浏览器受这个问题影响。如果在页面卸载以后事件处理程序没有被清理,则他们仍然会残留在内存里。之后浏览器每次加载和卸载页面,内存中残留对象数量都会增加,这是因为时间处理程序不会被回收。一般来说,最好在 onunload事件处理程序中趁页面尚未卸载先删除所有事件处理程序。关于卸载页面时的事件处理程序很少,所以很容易记住要删除哪些。关于卸载页面清理,可以记住一点:Onload事件做了什么,最好在onunload事件处理程序中恢复。

注意: 在页面中使用onunload事件处理程序意味着页面不会被保存在往返缓存中。如果这对应用很重要,可以只考虑IE浏览器。