Developing Silverlight 1.0 RIAs: JavaScript Optimization
(Read the intro here.)
My appreciation and respect for JavaScript increased tremendously while developing Silverlight projects. Not at all because of how easy it was to work with. But its flexibility and power made it interesting and fun to tackle challenges like optimization and application architecture.
Optimization
In Silverlight 1.0, the plugin is only a rendering and animation engine. All of the logic and much of the processing still resides in the browser’s JavaScript interpreter. All web applications need to make optimization a priority, but this is especially true for Silverlight 1.0 applications.
Doing a lot of small things can add up to make a big difference. Swap myArray.push(obj) with myArray[myArray.length] = obj. Avoid string concatenation when possible to give Internet Explorer a little boost.
I made two significant changes to my code between the WWE project and the last Library of Congress project that I believe made a big difference.
Avoid Function.bind
I usually resorted to a Function.bind method (usually the version in prototype.js) to manipulate function scope. This really comes in handy for event listeners:
-
// inside of a class
-
function addMouseEvents() {
-
this.xaml.addEventListener('MouseEnter', this.onMouseEnter.bind(this));
-
this.xaml.addEventListener('MouseLeave', this.onMouseLeave.bind(this));
-
this.xaml.addEventListener('MouseLeftButtonDown', this.onMouseDown.bind(this));
-
}
-
-
function onMouseEnter(sender, mouseArgs) {
-
// "this" still refers to the instance of the class
-
}
I really like this syntax, even though in an ideal world it wouldn’t be necessary. (Actionscript 3 is an ECMAScript implementation that fixes this). I’ve heard Mozilla might implement it natively, which would be lovely.
The problem with bind() is that it uses apply() internally, which is significantly slower than just executing the function. This makes a big difference for MouseMove or ProgressChanged event handlers.
Fortunately it is easy to avoid, and there are a number of ways to use depending on your personal preference. I usually used:
-
// inside of a class
-
function addMouseEvents() {
-
var that = this;
-
this.xaml.addEventListener('MouseEnter', function(sender, args) {
-
that.onMouseEvent(sender, args);
-
});
-
// .. other events
-
}
I looked around for any documentation on the effects on bind() on performance and found nothing. This isn’t a Silverlight-specific issue but something other people might run into so it’s probably worth documenting.
Using and Removing Event Listeners
This also isn’t necessarily Silverlight-specific, but it’s also worth mentioning because of a few difference from how HTML DOM event listeners are handled. The crux of the matter is still that adding event listeners is memory intensive and not disposing of them properly can cause leaks and sluggishness.
In HTML you can use event delegation to limit the number of event listeners added. Add a single click event listener to a parent object, and act upon the event based on on the target property of the event, which is usually a child of the parent object. This isn’t possible in Silverlight 1.0 because there isn’t a real event object. The sender argument sent to the listener is always the object to which the event was added.
The only way to combat the overhead from adding event listeners is to reuse objects when possible. For WWE, there is a big grid of thumbnails the user can click on. Instead of creating a whole new grid each page refresh, I just changed the data for each item.
Sometimes reuse isn’t an option, and you’re forced to create and destroy XAML objects repeatedly. Removing event listeners before destroying an object isn’t difficult.
-
var eventToken = xaml.addEventListener('MouseEnter', function() { … });
-
xaml.removeEventListener('MouseEnter', eventToken);
I came up with a way to store those event tokens in a simple, behind-the-scenes manner, which made this easier to do than not do. I’ll describe this in the next section on JavaScript/Silverlight Architecture.
- Intro
- Part 1: Xaml
- Part 2: JavaScript Optimization
- Part 3: JavaScript Architecture