a) 闭包的循环引用

闭包可能会导致在不经意间创建循环引用。因为函数是必须保存在内存中的对象,所以位于函数执行上下文中的所有变量也需要保存在内存中:
//闭包引起的循环引用

  1. function outerFn() {
  2. var outerVar = {};
  3. function innerFn() {
  4. console.log(outerVar);
  5. }
  6. outerVar.fn = innerFn;
  7. return innerFn;
  8. };

// 这里创建了一个名为 outerVar 的对象,该对象在内部函数innerFn()中被引用。然后,为 outerVar 创建了一个指向 innerFn()的属性,之后返回了innerFn()。这样就在 innerFn() 上创建了一个引用outerVar的闭包,而outerVar又引用了innerFn()。
//这会导致变量在内存中存在的时间比想象得长,而且又不容易被发现。这还不算完,还有可能会出现比这种情况更隐蔽的引用循环:

  1. function outerFn() {
  2. var outerVar = {};
  3. function innerFn() {
  4. console.log('hello');
  5. }
  6. outerVar.fn = innerFn;
  7. return innerFn;
  8. };

//这里我们修改了innerFn(),不再招惹 outerVar。但是,这样做仍然没有断开循环引用。
//即使innerFn()不再勾引 outerVar,outerVar 也仍然位于innerFn()的封闭环境中。由于闭包的原因,位于 outerFn()中的所有变量都隐含地被 innerFn()所引用。我们再想一想,在 java 中的内部类不也是类似当前情况吗,内部类能够‘看’外部的 this。此时此刻,正如彼时彼刻,竟如此相像。因此,闭包会使意外地创建这些引用循环变得易如反掌。

b) DOM与JavaScript的循环引用

//导致这种循环的一个常见原因是简单的事件处理:

  1. $(document).ready(function() {
  2. var button = document.getElementById('button-1');
  3. button.onclick = function() {
  4. console.log('hello');
  5. return false;
  6. };
  7. });

//当指定单击事件处理程序时,就创建了一个在其封闭的环境中包含button变量的闭包。而且,现在的button也包含一个指向闭包(onclick属性自身)的引用。这样,就导致了在IE中即使离开当前页面也不会释放这个循环。
//为了释放内存,就需要断开循环引用,例如关闭窗口,删除onclick属性。
//也可以像下面这样重写代码来避免这种闭包:

  1. function hello() {
  2. console.log('hello');
  3. return false;
  4. }
  5. $(document).ready(function() {
  6. var button = document.getElementById('button-1');
  7. button.onclick = hello;
  8. });

//因为hello()函数不再包含 button,引用就成了单向的(从button到hello),不存的循环,所以就不会造成内存泄漏
//用jQuery化解引用循环

  1. $(document).ready(function() {
  2. var $button = $('#button-1');
  3. $button.click(function(event) {
  4. event.preventDefault();
  5. console.log('hello');
  6. });
  7. });

//即使此时仍然会创建一个闭包,并且也会导致同前面一样的循环,但这里的代码却不会使 IE 发生内存泄漏。由于jQuery考虑到了内存泄漏的潜在危害,所以它会手动释放自己指定的所有事件处理程序。只要坚持使用jQuery的事件绑定方法,就无需为这种特定的常见原因导致的内存泄漏而担心。
注意点:低版本的IE中需要注意循环引用的问题