var element = document.getElementById("some_element");
var myObject = new Object();
myObject.element = element;
element.somObject = myObject;
这里例子在一个DOM元素(element)与一个原生的javascript对象(myObject)之间创建了循环引用。其中,变量myObject 有一个名为element的属性指向element对象;而变量element也有一个属性名叫someObject回指myObject。由于存在这个循环引用,即使将例子中的DOM从页面中移除,它也永远不会被回收。
为了避免类似这样的循环引用问题,最好是不使用他们的时候手工断开原生javascript对象与DOM元素之间的连接。例如,可以使用下面的代码消除前面例子创建的循环引用:
复制代码 代码如下:
myObject.element = null;
element.somObject = null;
将变量设置为null,意味着切断变量与它此前引用的值之间的连接。但垃圾收集器下次运行时,就会删除这些值并回收它们占用的内存。
性能问题
垃圾收集器都是周期性运行的,而且如果为变量分配的内存数量很客观,那么回收工作量也是相当大的。在这种情况下,确定垃圾收集的时间间隔是一个非常重要的问题。说到垃圾收集器多长时间运行一次,不禁让人联想到IE因此声名狼藉的性能问题。IE的垃圾收集器是根据内存分配量运行的,具体一点说就是256个变量、4096个对象(或数组)字面量和数组元素(slot)或者64KB的字符串。达到上述任何一个临界值,垃圾收集器就会运行。这种实现的问题在于,如果一个脚本中包含那么多变量,那么该脚本很可能会在其生命中起一支保持那么多的变量。而这样一来,垃圾收集器就可能不得不频繁的运行。结果,由此引发的严重性能问题初始IE7重写了其垃圾收集例程。
随着IE7的发布,其javascript引擎的垃圾收集例程改变了工作方式:触发垃圾收集的变量分配、字面量和(或)数组元素的临界值被调整为动态修正。IE7中的各项临界值在初始化时与IE6相等。如果例程回收的内存分配量低于15%,则变量 、字面量和(或)数组元素的临界值就会加倍。如果例程回收了85%的内存分配量,则将各种临界重置会默认值。这一看似简单的调整,极大地提升了IE在运行包含大量javascript的页面时的性能。
事实上,在有的浏览器中可以触发垃圾收集过程,当我们不建议读者这样做。在IE中,调用window.CollectGarbage()方法会立即指向垃圾收集,在Opera7及更高版本中,调用widnow.opera.collect()也会启动垃圾收集例程。
管理内存
使具备垃圾收集机制的语言编写程序,开发人员一般不必操心内存管理的问题。但是,javascript在进行内存管理及垃圾收集时面临的问题还是有点与众不同。其中最重要的一个问题,就是分配给web浏览器的可使用内存数量通常要比分配给桌面应用程序的少。这样做的目的出要是处于安全方面的考虑,目的是防止运行javascript的网页耗尽全部系统内存而导致系统崩溃。内存限制问题不仅会影响给变量分配内存,同时还会影响调用栈以及在一个线程中能够同时执行语句数量。
因此,确保占用最少内存可以让页面获得更好的性能,最好通过将其值设置为null来释放其引用——这个做法叫做解除引用(dereferencing)。这一做法是用于大多数全局变量和全局对象的属性。局部变量会在他们执行环境时自动被解除引用,如下面这个例子所示:
复制代码 代码如下:
function createPerson (name) {
var localPerson = new Object();
localPerson.name = name;
return localPerson;
};
var gllbalPerson = createPerson("Nicholas");
// 手工解除globalPerson的引用
globalPerson = null;
在这个例子中,变量globalPerson取得了createPerson()函数返回的值。在createPerson()函数内部,我们创建了一个对象并将其赋给了局部变量localPerson,然后又为该对象添加了一个名为name的属性。最后,当调用这个函数时,localPerson以函数的形式返回并赋给全局变量globalPerson。由于localPerson在createPerson()函数执行完毕后就离开了其执行环境,因此无需我们显示的去为他解除引用。但是对于全局变量globalPerson而言,则需要我们在不使用它的时候手工为它解除引用,这也正是上面例子中最后一行代码的目的。