AngularJS-$delete 删除事件侦听器吗?

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

通过侦听此事件,可以删除可能导致内存泄漏的事件侦听器。注册到作用域和元素的侦听器在被销毁时会自动清除,但是如果您在服务上注册了一个侦听器,或者在 DOM 节点上注册了一个没有被删除的侦听器,那么您必须自己清除它,否则您可能会引入内存泄漏。

最佳实践: 指令应该自己清理。您可以使用 element.on (“ $delete”,...)或 scope。当指令被删除时,运行一个清理函数。

问题:

在我的指令中有一个 element.on "click", (event) ->:

  1. 当指令被销毁时,是否有任何对 element.on的内存引用来防止它被垃圾收集?
  2. 角度文档指出,我应该使用一个处理程序来删除 $destroy发出的事件上的事件侦听器。我的印象是 destroy()删除了事件侦听器,难道不是这样吗?
117499 次浏览

事件监听器

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

  1. 范围事件监听器通过$on注册:

    $scope.$on('anEvent', function (event, data) {
    ...
    });
    
  2. Event handlers attached to elements via for example on or bind:

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

$scope.$destroy()

When $scope.$destroy() is executed it will remove all listeners registered via $on on that $scope.

It will not remove DOM elements or any attached event handlers of the second kind.

This means that calling $scope.$destroy() manually from example within a directive's link function will not remove a handler attached via for example element.on, nor the DOM element itself.


element.remove()

Note that remove is a jqLite method (or a jQuery method if jQuery is loaded before AngularjS) and is not available on a standard DOM Element Object.

When element.remove() is executed that element and all of its children will be removed from the DOM together will all event handlers attached via for example element.on.

It will not destroy the $scope associated with the element.

To make it more confusing there is also a jQuery event called $destroy. Sometimes when working with third-party jQuery libraries that remove elements, or if you remove them manually, you might need to perform clean up when that happens:

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

当指令被“销毁”时该怎么做

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

正常情况下,由于ng-view改变了当前视图,指令被销毁。当这种情况发生时,ng-view指令将销毁相关的$作用域,切断对其父作用域的所有引用,并在元素上调用remove()

这意味着当视图被ng-view销毁时,如果该视图的link函数中包含带有This的指令:

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


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

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

然而,重要的是要注意,这些侦听器中的代码仍然可能导致内存泄漏,例如,如果你已经实现了常见的JS内存泄漏模式circular references

即使在这种由于视图改变而导致指令被破坏的正常情况下,也可能需要手动清理一些东西。

例如,如果你在$rootScope上注册了一个监听器:

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


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

这是必需的,因为$rootScope在应用程序的生命周期内永远不会被销毁。

如果你正在使用另一个发布/订阅实现,当$作用域被破坏时,它不会自动执行必要的清理,或者如果你的指令向服务传递回调,情况也是一样的。

另一种情况是取消$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-viewng-if销毁。

如果你有自定义指令来管理DOM元素的生命周期等,当然会变得更复杂。