Javascript 为什么即使触发$destroy后作用域也不会被销毁?

Javascript 为什么即使触发$destroy后作用域也不会被销毁?,javascript,jquery,angularjs,Javascript,Jquery,Angularjs,我已经做了一个指令,当单击时,会创建一个对话框,该对话框使用jQuery附加到主体中。问题是,当对话框关闭时,作用域从未正确清理。如下图所示,保留了167个子范围。与包含ng repeat指令的对话框中的项目数量相匹配 我试图在上创建一个非常简单的场景版本。令我惊讶的是,事实上,在Plnkr中,每次关闭时,示波器都会被移除。因此,在生产中的某个地方,即使调用了$destroy,作用域仍保持活动状态 link: ($scope, $element, $attr) -> $element

我已经做了一个指令,当单击时,会创建一个对话框,该对话框使用jQuery附加到主体中。问题是,当对话框关闭时,作用域从未正确清理。如下图所示,保留了167个子范围。与包含ng repeat指令的对话框中的项目数量相匹配


我试图在上创建一个非常简单的场景版本。令我惊讶的是,事实上,在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
链接:($scope,$element,$attr)->
$element.on'click',()->
$scope.$apply()->
child=$scope.$new()
模板“”“…列出对话框内容…”
compiledTemplate=$compile(模板)(子级)
container=containers.createDialogContainer($element)
container.append(compiledTemplate)
#清理
$scope.closeWidget=()->
container.trigger(“container\u close”)
返回
container.on“container\u close”,()->
子项。$apply()->
child.$destroy()
返回
所以我的问题是:

调用、触发$destroy并执行垃圾收集后,什么会导致作用域保持活动状态?

由于明显的原因,我无法向您展示我们的生产代码。但是,Plnkr中的指令与im调试中的指令充分匹配。

一般来说,如果范围(或任何其他JS对象)仍然可以由另一个JS对象访问,则GC无法清理该范围

实际上,在同样使用JQuery的Angular项目中,这很可能是由以下原因造成的:

  • 角度服务、控制器、范围或某些其他对象仍有对范围对象的引用
  • 对对象的引用仍然通过DOM元素存在,可能通过事件侦听器存在。DOM元素本身可能不支持GC,因为它仍在JQuery的缓存中
例如,您的示例创建了内存泄漏

从您的代码:

 $scope.removeDialog = () ->
    console.log "closing"
    child.$destroy()
    $('.listshell').remove()
    return
您没有将
child
设置为
null
,因此
$scope.removeDialog
仍然可以访问
child
变量引用的scope对象。因此,无法对该对象进行GC'ed


注意:在我看来,将
removeDialog
放在子作用域上更合适。现在,您的示例之所以有效,是因为子作用域没有被隔离。

闭包函数可以使函数激活对象保持活动状态,即使在作用域被“销毁”之后也是如此。例如,您可能有内部函数仍在引用您试图销毁其作用域的函数中的变量对象


与删除相比,取消引用变量是最好的选择。我能想到的唯一一件事是,如果在控制器或指令之外的某个地方有一个全局函数或函数引用了指令作用域内的某个方法,那么它实际上会使该作用域在整个过程中保持活动状态应用程序。

“令我惊讶的是,在Plnkr中的每次关闭时,作用域实际上都被删除。因此,在生产中的某个地方,即使调用$destroy之后,作用域仍保持活动状态。”-->,如果没有心理调试,我们无法解决您的问题。@GeorgeStocker问题是我没有寻找具体的解决方案。我正在寻找这个问题的可能答案:“什么可以使作用域保持活动?”。我意识到,根据所提供的信息,不可能准确指出我的问题所在。然而,我希望有人在过去经历过“清理”角度作用域的问题,并能对如何解决该问题有所帮助。根据文档销毁事件:“从父作用域中删除当前作用域(及其所有子作用域)。删除意味着调用$digest()将不再传播到当前作用域及其子项。删除还意味着当前作用域有资格进行垃圾收集。“。它符合垃圾收集的条件,并不意味着它已经被删除。我认为这是调试策略必须删除一半代码的时候之一,看看它是否仍然发生,如果是这样,则删除另一半代码,等等,直到您将问题隔离到一个小示例为止。然后,如果您还有问题,请用代码的这一部分更新您的问题。@GeorgeStocker我认为这是一个真正的问题,它实际上有一个答案,他希望销毁事件发生时作用域会消失,但事实并非如此,这并不意外,事实上,在这种情况下,我认为这是没有必要的。我只是有同样的问题。我的问题是引导模式。模态中的作用域在模态关闭时被销毁,但仍在jQuery模态事件侦听器中被引用,而没有获得GC的ID。在modal close上重置这些事件侦听器允许GC删除已销毁的作用域。