msdnmagazine.com February 2014 43
memory leak occurs if a reference to an object (and therefore the
object itself ) remains beyond when it’s needed.
One common cause of memory leaks in JavaScript applications
are “zombie” objects, which typically occur when a JavaScript object
references a DOM object and that DOM object is removed from
the document (through a call to removeChild or innerHTML). Th e
corresponding JavaScript object remains in memory, even though
the corresponding HTML has vanished:
var newSpan = document.createElement("span");
document.getElementById("someDiv").appendChild(newSpan);
document.getElementById("someDiv").innerHTML = "";
WinJS.log && WinJS.log(newSpan === "undefined");
// The previous statement outputs false to the JavaScript console.
// The variable "newSpan" still remains even though the corresponding
// DOM object is gone.
For a normal Web page, the life of an object extends only for as
long as the browser displays the page. Windows Store apps can’t
ignore these sorts of memory leaks. Apps commonly use a single
HTML page as a content host, where that page persists throughout
the app session (which could last for days, or even months). If an
app changes state (the user navigates from one page to another, for
example, or a ListView control is scrolled so that some items fall out
of visibility) without cleaning up memory allocated to unneeded
JavaScript objects, that memory can become unavailable to the app.
Checking for Memory Leaks
Luckily, Visual Studio 2013 has new features that can help developers
track down memory leaks—in particular the Performance and
Diagnostics window. For this test case and the next, I’ll demon-
strate a couple of the tools that it surfaces.
For this test case, I’ll add a custom control to my solution that
purposely allows memory leaks. Th is control, named SearchLOC-
Control (/js/SearchLOCControl.js), creates a search text box and
then displays results aft er a response to a query has been received.
<div data-win-control="WinJS.UI.NavBar">
<div data-win-control="WinJS.UI.NavBarContainer">
<!-- Other NavBarCommand elements. -->
<div id="dispose"
data-win-control="WinJS.UI.NavBarCommand"
data-win-options="{
location: '/pages/dispose/dispose.html',
icon: 'delete',
label: 'Dispose pattern in JS'
}">
</div>
<div id="scheduler"
data-win-control="WinJS.UI.NavBarCommand"
data-win-options="{
location: '/pages/scheduler/scheduler.html',
icon: 'clock',
label: 'Scheduler'
}">
</div>
<div id="worker"
data-win-control="WinJS.UI.NavBarCommand"
data-win-options="{
location: '/pages/worker/worker.html',
icon: 'repair',
label: 'Web worker'
}">
</div>
</div>
</div>
Figure 1 Additional NavBarCommands in Default.html
Figure 2 Accessing the Print & Photographs Online Catalog Web Service
(function () {
"use strict";
var baseUrl = "http://loc.gov/pictures/"
var httpClient = new Windows.Web.Http.HttpClient();
function searchPictures(query) {
var url = baseUrl + "search/?q=" + query + "&fo=json";
var queryURL = encodeURI(url);
return httpClient.getStringAsync(
new Windows.Foundation.Uri(queryURL)).
then(function (response) {
return JSON.parse(response).results.map(function (result) {
return new SearchResult(result);
});
});
}
function getCollections() {
var url = baseUrl + "?fo=json";
return httpClient.getStringAsync(new Windows.Foundation.Uri(url)).
then(function (response) {
return JSON.parse(response).featured.
map(function (collection) {
return new Collection(collection);
});
});
}
function getCollection(collection) {
var url = baseUrl + "search/?co=" + collection.code + "&fo=json";
var queryUrl = encodeURI(url);
return httpClient.getStringAsync(new Windows.Foundation.Uri(queryurl)).
then(function (response) {
collection.pictures = JSON.parse(response).
results.map(function (picture) {
return new SearchResult(picture);
});
return collection;
});
}
function Collection(info) {
this.title = info.title;
this.featuredThumb = info.thumb_featured;
this.code = info.code;
this.pictures = [];
}
function SearchResult(data) {
this.pictureThumb = data.image.thumb;
this.title = data.title;
this.date = data.created_published_date;
}
WinJS.Namespace.define("LOCPictures", {
Collection: Collection,
searchPictures: searchPictures,
getCollections: getCollections,
getCollection: getCollection
});
})();