AngularJS-$ destroy是否会删除事件监听器?


200

https://docs.angularjs.org/guide/directive

通过侦听此事件,可以删除可能导致内存泄漏的事件侦听器。注册到范围和元素的侦听器在销毁时会自动清除,但是如果您在服务上注册了侦听器,或者在未删除的DOM节点上注册了侦听器,则必须自己清理或您可能会引入内存泄漏的风险。

最佳实践:指令应自行清理。删除指令后,可以使用element.on('$ destroy',...)或scope。$ on('$ destroy',...)运行清理功能。

题:

我有element.on "click", (event) ->我的指令内:

  1. 伪指令销毁后,是否有任何对的内存引用,element.on以防止其被垃圾回收?
  2. Angular文档指出,我应该使用处理程序删除$destroy发出的事件上的事件侦听器。我的印象是destroy()删除了事件监听器,不是吗?

Answers:


433

事件监听器

首先,重要的是要了解有两种“事件侦听器”:

  1. 范围事件侦听器通过$on以下方式注册:

    $scope.$on('anEvent', function (event, data) {
      ...
    });
    
  2. 通过例如on或附加到元素的事件处理程序bind

    element.on('click', function (event) {
      ...
    });
    

$ scope。$ destroy()

$scope.$destroy()执行时,它将删除通过$on该$ scope 注册的所有侦听器。

不会删除DOM元素或任何第二种附加的事件处理程序。

这意味着$scope.$destroy()从指令的链接函数内的示例中手动调用将不会删除通过example附加的处理程序element.on,也不会删除DOM元素本身。


element.remove()

请注意,这remove是一个jqLit​​e方法(如果jQuery在AngularjS之前加载,则为jQuery方法),并且在标准DOM元素对象上不可用。

element.remove()执行该元素及其所有子元素时,将从所有DOM中一起删除所有事件处理程序,例如element.on

不会破坏与该元素关联的$ scope。

为了使其更加混乱,还有一个名为的jQuery事件$destroy。有时,当使用删除元素的第三方jQuery库时,或者如果您手动删除它们,则可能需要在发生这种情况时执行清理:

element.on('$destroy', function () {
  scope.$destroy();
});

指令被“销毁”时该怎么办

这取决于该指令如何被“销毁”。

通常的情况是由于ng-view更改当前视图而导致指令被销毁。发生这种情况时,ng-view指令将销毁关联的$ scope,切断对其父范围的所有引用并调用remove()元素。

这意味着,如果该视图在被以下命令破坏时,其链接函数中包含带有此指令的指令ng-view

scope.$on('anEvent', function () {
 ...
});

element.on('click', function () {
 ...
});

这两个事件监听器都将被自动删除。

但是,请务必注意,这些侦听器中的代码仍然会导致内存泄漏,例如,如果您已经实现了常见的JS内存泄漏模式circular references

即使在正常情况下,由于视图更改而导致指令被销毁的情况下,也可能需要手动清除某些内容。

例如,如果您在上注册了侦听器$rootScope

var unregisterFn = $rootScope.$on('anEvent', function () {});

scope.$on('$destroy', unregisterFn);

这是必需的,因为$rootScope它在应用程序的生命周期中从未被破坏。

如果您使用的是另一个pub / sub实现,当$ scope被销毁时,或者您的指令将回调传递给服务,则该实现也不会自动执行必要的清理,情况也是如此。

另一种情况是取消$interval/ $timeout

var promise = $interval(function () {}, 1000);

scope.$on('$destroy', function () {
  $interval.cancel(promise);
});

如果您的指令将事件处理程序附加到例如当前视图之外的元素,则还需要手动清除它们:

var windowClick = function () {
   ...
};

angular.element(window).on('click', windowClick);

scope.$on('$destroy', function () {
  angular.element(window).off('click', windowClick);
});

这些是Angular(例如ng-view或)“销毁”指令时的操作示例ng-if

如果您具有管理DOM元素等生命周期的自定义指令,那么它当然会变得更加复杂。


4
“ $ rootScope永远不会在应用程序的生存期内被破坏。” :一旦想到它就很明显。那就是我所缺少的。
user276648'2

@tasseKATT这里有个小问题,如果在同一个控制器中,我们有多个$ rootScope。$ on用于不同的事件,那么我们应该调用$ scope。$ on(“ $ destroy”,ListenerName1);。对于每个$ rootScope。$ on不同?
Yashika Garg 2015年

2
@YashikaGarg拥有一个调用所有侦听器的帮助器功能可能是最简单的。像$ scope。$ on('$ destroy'),function(){ListenerName1(); ListenerName2(); ...})非隔离范围上的$ on事件处理程序是否还有其他复杂性?还是用两种方式隔离作用域?
David Rice

为什么要在$ rootscope上注册事件监听器?我在$ scope上注册事件监听器,然后其他控制器执行$ rootscope.broadcast('eventname')并运行我的事件监听器。$ scope上的这些正在监听应用程序事件的事件监听器是否仍将自动清除?
Skychan 2015年

@Skychan对不起,我错过了您的评论。这是一个猜测,但人们可能会$rootScope因为以下原因而使用:stackoverflow.com/questions/11252780/…请注意,由于答案位于顶部,因此已更改。是的,$scope当该作用域销毁时,将自动清除法线上的事件侦听器。
tasseKATT
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.