澄清重新评估循环参考

时间:2014-06-14 09:44:23

标签: javascript circular-reference

我不认为我完全理解这个概念。

我知道有什么东西需要彼此导致无限循环,但我不知道在我查看的一些例子中会发生这种情况

function setHandler() {  
  var elem = document.getElementById('id')

  elem.onclick = function() {
    // ...
  }
}

这是怎么回事?我只是看到一个元素在点击时附加了一个函数。

Screenshot

它说通过外部LexicalEnvironment有一个引用 - 但这不是一次发生的吗?

提前致谢。

2 个答案:

答案 0 :(得分:1)

  

这是怎么回事?

这是IE 6早期版本中的一个主要问题,因为它创建了内存泄漏"因为涉及DOM元素的循环引用导致在卸载页面时内存未被释放,因此它们消耗了越来越多的内存,从而使浏览器的响应性降低。这篇文章很here

关于OP中的特定模式,很容易避免循环引用:

function setHandler() {  
  var elem = document.getElementById('id')

  elem.onclick = function() {
    // ...
  }

  // Break circular reference
  elem = null;
}

答案 1 :(得分:0)

JavaScript使用一种称为词法范围的东西。范围

  • 定义在特定上下文中可见的变量(推论:它们定义哪些变量不再对任何人可见,并且可以安全地进行垃圾收集)
  • 为每个function 创建
  • (每次使用关键字时,都会创建新范围)
  • 可以嵌套(因为函数可以嵌套)
  • 是为每个函数保留的(当你调用一个函数时,它保证它可以看到所有范围内的所有变量(!)在创建它时可用)

这意味着:

                                            // +- global scope
                                            // |
function setHandler() {                     // |+- scope for setHandler()
                                            // ||  sees: own, global
  var elem = document.getElementById('id'); // ||
                                            // ||
  elem.onclick = function() {               // ||+- Scope for anonymous function
    // ...                                  // |||  sees: own, setHandler, global
  }                                         // |||
}                                           // ||
                                            // |

现在,您分配给onclick的匿名函数可以看到变量elemsetHandler以及全局范围内的所有内容。

如果元素被销毁(从DOM中删除),垃圾收集器会考虑哪些变量超出范围。较低级的垃圾收集器可以通过保留任何内存中对象的引用计数并在引用计数达到零时清理该对象(释放内存)来实现此目的。

现在无法清除匿名点击处理函数,因为它保存了对elem(通过范围)的引用,而onclick依次保存对匿名函数的引用(通过其setHandler()属性)。两个对象的引用计数保持为1,因为这两个对象相互引用。

注意上面的第4点:范围是持久的,所以"词汇环境"即使程序执行已经离开该函数,{{1}}仍保持活动 - 因为匿名内部函数需要它。 (这被称为"封闭"并且是JS的基石之一。)

现代垃圾收集器可以发现这种情况并适当地清理事件处理程序,但特别是旧版本,如果IE行为不当并泄露了内存。