Why do I see so many AS3 programmers writing so much redundant code? Are you one of them? Today’s tips may save you a lot of typing. It may even save you a lot of SWF size.

There’s a certain unease programmers get about some language features. Maybe you come from a language like C or C++ where variables are not automatically given default values and it worries you when you see code like this:

class Person
{
	public var verboseLogging:Boolean;
	private var name:String;
	public function set name(newName:String): void
	{
		if (verboseLogging)
		{
			trace("Changing name from " + name + " to " + newName);
		}
		name = newName;
	}
}

The cry will go out: “what if the user never sets verboseLogging!?” Thankfully, we know that is is redundant to initialize verboseLogging to false because the Flash Player will do it for us. In C/C++, accessing verboseLogging would result in undefined behavior since that memory could have been set to anything before. In AS3, we’re guaranteed that it’ll be false.

“I’ve known that since you wrote about it in 2009″, you may rightly say. Or maybe you knew about it even before that. Well, feel free to shake your head every time you see someone’s AS3 code redundantly initializing a variable. But before you get too presumptuous, let’s talk about a couple more areas where you may be writing redundant code. First, let’s consider some if statements. How many of these do you write?

function verbose(b:Boolean, o:Object, s:String, i:int, n:Number): void
{
	if (b == true) {}
	if (b == false) {}
 
	// UPDATE: this should really be a Point or some other Object derivative
	if (o != null) {}
	if (o == null) {}
 
	if (s != null && s.length > 0) {}
	if (s == null || s.length == 0) {}
 
	if (i != 0) {}
	if (i == 0) {}
 
	if (n != 0 && !isNaN(n)) {}
	if (n == 0 || isNaN(n)) {}
}

All of these can be written with less typing and less bytecode. Consider these alternatives:

function concise(b:Boolean, o:Object, s:String, i:int, n:Number): void
{
	if (b) {}
	if (!b) {}
 
	if (o) {}
	if (!o) {}
 
	if (s) {}
	if (!s) {}
 
	if (i) {}
	if (!i) {}
 
	if (n) {}
	if (!n) {}
}

All of these (even strings) are functionally equivalent and take advantage of AS3 bytecode’s iftrue and iffalse instructions’ support for non-Boolean types rather than pushing an extra argument (true, false, null, etc.) and comparing with an ifeq or ifne instruction. Consider the bytecode for the if (o != null) test: (which jumps over a block of code when o is null)

// verbose
    14      getlocal2     	
    15      pushnull      	
    16      ifeq          	L3
 
// concise
    12      getlocal2     	
    13      iffalse       	L3

In addition to less typing, there’s less bytecode to bloat up the SWF. One instruction isn’t much bloat on its own, but over thousands or tens of thousands of lines of code it really adds up. Is there a corresponding performance loss to add up too? Well, let’s look at a quick test:

package
{
	import flash.display.*;
	import flash.utils.*;
	import flash.text.*;
 
	public class VerboseConcisePerformance extends Sprite
	{
		private var __logger:TextField = new TextField();
		private function log(msg:*): void { __logger.appendText(msg + "\n"); }
 
		public function VerboseConcisePerformance()
		{
			__logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(__logger);
 
			const REPS:int = 100000000;
			var beforeTime:int;
			var afterTime:int;
			var i:int;
			var nullObj:VerboseConcisePerformance;
			var notNullObj:VerboseConcisePerformance = this;
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				if (nullObj != null) {}
			}
			afterTime = getTimer();
			log("verbose null: " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				if (notNullObj != null) {}
			}
			afterTime = getTimer();
			log("verbose not null: " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				if (nullObj) {}
			}
			afterTime = getTimer();
			log("concise null: " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				if (notNullObj) {}
			}
			afterTime = getTimer();
			log("concise not null: " + (afterTime-beforeTime));
		}
	}
}

Here are the results I get with the Flash Player 10.1.102.64 release plugin on a 2.4 Ghz Intel Core i5 running Mac OS X 10.6:

Verbose Concise
Null Non-Null Null Non-Null
215 213 210 210

When you’re running a loop a hundred million times and finding performance differences like 2 milliseconds (depending on test run), you haven’t really found a performance difference. I’d speculate that the JIT is optimizing one version into the other.

One last little application of the above: you don’t need to initialize your loop iterator! Consider a standard for loop:

for (var i:int = 0; i < len; ++i)
{
}

I don’t know about you, but I can type that loop in my sleep. OK, maybe you use i++ instead. Coming from C/C++, it’s hard for me to remember that I don’t need to initialize i. This loop is equivalent:

for (var i:int; i < len; ++i)
{
}

But there is a gotcha here: variables are “hoisted” to the function level in AS3, so you can only get away with not initializing i on the first loop. Subsequent loops need their iterators initialized because they will re-use i. For example, this will fail miserably:

for (var i:int; i < len; ++i)
{
}
for (var i:int; i < len; ++i)
{
}

The i iterator will end the first loop with a value of len and not be set back to 0 for the second loop. You’l get a compiler warning from MXMLC telling you that you’re re-using i, so at least you’ll be warned about the problem at compile time.

In conclusion:

  • Don’t redundantly initialize your variables
  • Don’t do needless checks in your if statements (unless you really like the aesthetics)

I hope you’ve read something in today’s article that will save you some typing, some SWF size, or both.