I've been writing lots of Angular directives recently and have been reading about having to manually remove things you create in a directive when it is destroyed. It's still a little unclear though.
I know that anything palced on $scope is destroyed, but what about local variables, arrays, functions etc created in a directive's controller?
Here is some example code from my app
angular.module('myModule').controller('s4pFileUploadCtrl', function($scope, $element, $attrs, FileUploader){
var self = this;
self.uploader = new FileUploader({
settings: {}
});
// Elsewhere in the controller
var reader = new FileReader();
reader.onload = onLoadFile;
function onLoadFile(event) {
var img = new Image();
img.onload = onLoadImage;
}
});
This is a contrived and very much cut down function, but do I need to remove the uploader on $scope destroy event? DO i need to do anything about the FileReader and it's associated onLoadFile event?
Any help would be appreciated. I've read loads of articles about cleaning up directives but none of them mention things like this.
Here's a great article for some background on javascript memory management and garbage collection more generally. Basically, it works something like this...
Memory will be allocated for an object when created:
var o = {iThink: 'thereforeIAm'}; // memory allocated for an object and a string
You may then console.log(o.iThink)
and 'thereforeIAm'
will be read from its place in memory and printed to the console.
If you wanted to create a new string and lost the need for the {iThink: 'thereforeIAm'}
object, you may decide to overwrite o
instead of introducing a new variable e.g:
o = 'helloImANewString';
Fortunately, references (or lack thereof) send a clear message internally to javascript's garbage collector as to whether a piece of (finite) memory should remain allocated or freed to perform other tasks. In this case, no reference remains to the object and string previously allocated for {iThink: 'thereforeIAm'}
and the corresponding memory may be freed (i.e. "garbage-collected") as result.
Importantly, note that garbage collection happens internally. No code has to be written by you to that effect. All you need to concern yourself with is the value of o
and the garbage collector can infer need, more or less, from remaining reference.
Unfortunately, the cleanup tasks related to $scope
removal cannot be inferred by the javascript garbage collector based on reference alone; additional code is required.
That is because $scope objects embody a concept more complex than any ol' javascript object. In particular, when no use remains in your application for a certain $scope
object, no use must also remain for any associated $$watchers
previously registered with the $scope.$watch
method and no use must also remain for "children" $scope
objects. The javascript garbage collector cannot infer this relationship from simple reference removal e.g:
$scope = null; // $scope object will be garbage collected, but nothing else
In other words, the garbage collector has to be told what to do, which is exactly what the $scope.$destroy method does. Note these lines in particular:
$scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
$scope.$$childTail = $scope.$root = $scope.$$watchers = null;
I know that anything placed on $scope is destroyed, but what about local variables, arrays, functions etc created in a directive's controller?
Yes, but not directly by the $destroy method.
When a function is invoked, the memory allocated for internally scoped objects will only remain allocated to the extent references remain past the life of the invocation.
In the example of your controller, in fact, no references remain to reader
at all after the controller function is run whether or not $scope
is $destroy
'ed. But even if you did attach it to $scope
as you did with uploader
, the only remaining reference thereto results from such attachment to $scope. Consequently, any call to $scope.$destroy()
would remove the last reference to uploader
(by removing any reference to $scope
) and render it eligible for javascript garbage collection.