为什么即使在$ destroy被触发后范围也没有被破坏?

时间:2014-02-18 13:46:35

标签: javascript jquery angularjs

我做了一个指令,当点击它时,会创建一个使用jQuery附加到正文的对话框。问题是当关闭对话框时,范围永远不会被正确清理。如下图所示167 ChildScopes被保留。这与对话框中包含ng-repeat指令的项目数相匹配。

enter image description here

我试图在Plnkr上创建一个极其简单的场景版本。令我惊讶的是,实际上,在Plnkr的每个关闭处都移除了范围。因此,即使在调用$destroy之后,生产中的某个地方也会导致范围保持活跃状态​​。

link: ($scope, $element, $attr) ->
  $element.on 'click', () ->
      $scope.$apply () ->
        child = $scope.$new()
        template = """<span ng-controller="ListCtrl">...List dialog things...</span>"""
        compiledTemplate = $compile(template)(child)
        container = containers.createDialogContainer($element)
        container.append(compiledTemplate)

        #cleanup
        $scope.closeWidget = () ->
          container.trigger("container_close")
          return

        container.on "container_close", ()->
          child.$apply () ->
            child.$destroy()
          return

所以这是我的问题:

即使在调用$ destroy,触发和执行垃圾收集后,什么可以导致范围保持活动?

由于显而易见的原因,我无法向您展示我们的生产代码。然而,Plnkr中的指令与我的调试充分匹配。

3 个答案:

答案 0 :(得分:5)

通常,如果另一个JS对象仍然可以访问GC,则无法清除作用域(或任何其他JS对象)。

实际上,在同样使用JQuery的Angular项目中,这很可能是由:

引起的
  • 一个Angular服务,控制器,作用域或其他一些仍然引用了您的作用域对象的对象
  • 对您的对象的引用仍然通过DOM元素存在,可能通过事件侦听器。 DOM元素本身可能不是GC,因为它仍然在JQuery的缓存中

例如,您的示例会造成内存泄漏。

从你的代码:

 $scope.removeDialog = () ->
    console.log "closing"
    child.$destroy()
    $('.listshell').remove()
    return

您没有将child设置为null,因此$scope.removeDialog仍然可以访问child变量引用的范围对象。因此,此对象不能用于GC。

注意:在我看来,将removeDialog放在子范围上会更合适。现在,您的示例仅起作用,因为子范围未被隔离。

答案 1 :(得分:0)

关闭函数可以使函数激活对象在范围被“销毁”后仍然保持活动状态。例如,您可能还有内部函数仍然在您试图销毁其范围的函数中引用变量对象。

使引用变量无效将是最佳选择,而不是删除

答案 2 :(得分:0)

我唯一能想到的是,如果某个地方的控制器或指令之外的某个全局函数或函数引用了该指令范围内的方法,那么它将使该范围保持活动状态。应用