事件监听器
首先,重要的是要了解有两种“事件侦听器”:
-
通过$on注册的范围事件监听器:
$scope.$on('anEvent', function (event, data) {
...
});
-
通过例如on 或bind 附加到元素的事件处理程序:
element.on('click', function (event) {
...
});
$scope.$destroy()
当$scope.$destroy() 被执行时,它将移除所有通过$on 在该$scope 上注册的监听器。
它不会删除 DOM 元素或任何附加的第二类事件处理程序。
这意味着在指令的链接函数中从示例手动调用$scope.$destroy() 不会删除通过例如element.on 附加的处理程序,也不会删除DOM 元素本身。
element.remove()
请注意,remove 是一个 jqLite 方法(如果 jQuery 在 AngularjS 之前加载,则为 jQuery 方法)并且在标准 DOM 元素对象上不可用。
当element.remove() 执行时,该元素及其所有子元素将从DOM 中删除,所有事件处理程序都将通过例如element.on 附加。
它不会破坏与元素关联的 $scope。
为了让它更加混乱,还有一个名为$destroy 的jQuery 事件。有时在使用删除元素的第三方 jQuery 库时,或者如果您手动删除它们,您可能需要在发生这种情况时执行清理:
element.on('$destroy', function () {
scope.$destroy();
});
指令被“销毁”时该怎么办
这取决于指令是如何“销毁”的。
正常情况是指令被销毁,因为ng-view 更改了当前视图。发生这种情况时,ng-view 指令将销毁关联的 $scope,切断对其父范围的所有引用并在元素上调用 remove()。
这意味着如果该视图在被ng-view 销毁时在其链接函数中包含带有 this 的指令:
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 元素等生命周期的自定义指令,它当然会变得更加复杂。