Currently working on a project where we found huge memory leaks when not clearing broadcast subscriptions off destroyed scopes. The following code has fixed this:
var onFooEventBroadcast = $rootScope.$on('fooEvent', doSomething); scope.$on('$destroy', function() { //remove the broadcast subscription when scope is destroyed onFooEventBroadcast(); });
Should this practice also be used for watches? Code example below:
var onFooChanged = scope.$watch('foo', doSomething); scope.$on('$destroy', function() { //stop watching when scope is destroyed onFooChanged(); });
What is the angular JS watch function? The angular JS $watch function is used to watch the scope object. The $watch keep an eye on the variable and as the value of the variable changes the angular JS $what runs a function. This function takes two arguments one is the new value and another parameter is the old value.
$watch() function is used to watch the changes of variables in $scope object. Generally the $watch() function will create internally in Angularjs to handle variable changes in application.
AngularJS Scope The scope is the binding part between the HTML (view) and the JavaScript (controller). The scope is an object with the available properties and methods. The scope is available for both the view and the controller.
@setec . Yes you are right, $destroy() should be called only when a child scope has been created manually. Also, in most cases the child scope is created automatically every time we use Directives. (e.g ng-controller, ng-app) and hence $destroy() function is called automatically.
No, you don't need to remove $$watchers
, since they will effectively get removed once the scope is destroyed.
From Angular's source code (v1.2.21), Scope
's $destroy
method:
$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 = []; ...
So, the $$watchers
array is emptied (and the scope is removed from the scope hierarchy).
Removing the watcher
from the array is all the unregister function does anyway:
$watch: function(watchExp, listener, objectEquality) { ... return function deregisterWatch() { arrayRemove(array, watcher); lastDirtyWatch = null; }; }
So, there is no point in unregistering the $$watchers
"manually".
You should still unregister event listeners though (as you correctly mention in your post) !
NOTE: You only need to unregister listeners registered on other scopes. There is no need to unregister listeners registered on the scope that is being destroyed.
E.g.:
// You MUST unregister these $rootScope.$on(...); $scope.$parent.$on(...); // You DON'T HAVE to unregister this $scope.$on(...)
(Thx to @John for pointing it out)
Also, make sure you unregister any event listeners from elements that outlive the scope being destroyed. E.g. if you have a directive register a listener on the parent node or on <body>
, then you must unregister them too.
Again, you don't have to remove a listener registered on the element being destroyed.
Kind of unrelated to the original question, but now there is also a $destroyed
event dispatched on the element being destroyed, so you can hook into that as well (if it's appropriate for your usecase):
link: function postLink(scope, elem) { doStuff(); elem.on('$destroy', cleanUp); }
I would like to add too @gkalpak's answer as it lead me in the right direction..
The application I was working on created a memory leak by replacing directives whom had watches. The directives were replaced using jQuery and then complied.
To fix i added the following link function
link: function (scope, elem, attrs) { elem.on('$destroy', function () { scope.$destroy(); }); }
it uses the element destroy event to in turn destroy the scope.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With