当范围被破坏时,是否应该删除angular $ watch?

And*_*rew 53 javascript angularjs angularjs-directive angularjs-scope

目前正在开展一个项目,当我们发现大量内存泄漏时,不清除已销毁范围内的广播订阅.以下代码修复了此问题:

var onFooEventBroadcast = $rootScope.$on('fooEvent', doSomething);

scope.$on('$destroy', function() {
    //remove the broadcast subscription when scope is destroyed
    onFooEventBroadcast();
});
Run Code Online (Sandbox Code Playgroud)

这种做法是否也应该用于手表?代码示例如下:

var onFooChanged = scope.$watch('foo', doSomething);

scope.$on('$destroy', function() {
    //stop watching when scope is destroyed
    onFooChanged();
});
Run Code Online (Sandbox Code Playgroud)

gka*_*pak 87

不,您不需要删除$$watchers,因为一旦范围被销毁,它们将被有效地删除.

从角的源代码(v1.2.21),Scope$destroy方法:

$destroy: function() {
    ...
    if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
    if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
    if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
    if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
    ...
    this.$$watchers = this.$$asyncQueue = this.$$postDigestQueue = [];
    ...
Run Code Online (Sandbox Code Playgroud)

因此,$$watchers清空数组(并从范围层次结构中移除范围).

watcher无论如何,从数组中删除所有取消注册函数都会执行:

$watch: function(watchExp, listener, objectEquality) {
    ...
    return function deregisterWatch() {
        arrayRemove(array, watcher);
        lastDirtyWatch = null;
    };
}
Run Code Online (Sandbox Code Playgroud)

因此,取消注册$$watchers"手动" 是没有意义的.


您仍然应该取消注册事件侦听器(正如您在帖子中正确提到的那样)!

注意: 您只需要取消注册在其他范围上注册的侦听器.无需取消注册正在销毁的作用域上的侦听器.
例如:

// You MUST unregister these
$rootScope.$on(...);
$scope.$parent.$on(...);

// You DON'T HAVE to unregister this
$scope.$on(...)
Run Code Online (Sandbox Code Playgroud)

(感谢@John 指出它)

此外,请确保从与被销毁范围相比的元素中取消注册任何事件侦听器.例如,如果您有一个指令在父节点上或在其上注册一个侦听器<body>,那么您也必须取消注册它们.
同样,您不必删除在要销毁的元素上注册的侦听器.


与原始问题无关的类型,但现在还有一个$destroyed事件在被销毁的元素上发送,所以你也可以挂钩(如果它适合你的用例):

link: function postLink(scope, elem) {
  doStuff();
  elem.on('$destroy', cleanUp);
}
Run Code Online (Sandbox Code Playgroud)

  • 实际上,似乎**没有必要**取消注册事件监听器**如果它们在被销毁的范围内注册**.我的意思是:`$ scope.$ on('fooEvent',doSomething); //无需取消注册``$ rootScope.$ on('fooEvent',doSomething); // $ rootScope永远不会被破坏,你需要取消注册这个:见http://stackoverflow.com/questions/26983696/angularjs-does-destroy-remove-event-listeners http://www.bennadel.com/博客/ 2723-解除绑定范围的-on事件处理者,在-angularjs.htm (2认同)