February 2004


ActionScript 2.0 is much more object oriented and Java-like than its predecessor, which makes it easy to learn for Java programmers. Here are some key differences, which may help make the transition easier:

Static Classes

Although AS2.0 does support static classes, a crucial difference from Java is that static members cannot be accessed through instance variables. For example, if we have a Java class Foo that defines a public static field BAR, the following would output the value of BAR:

Foo myFoo = new Foo();
System.out.println("BAR is "+myFoo.BAR);

However, this will not work in AS2.0 since static members can’t be accessed through instance variables. Instead, we’d only be able to access BAR through the static reference Foo.BAR. In my opinion, this is a significant shortcoming that breaks some important design patterns that rely on static members.

No Multithreading

ActionScript is not multithreaded. This relieves us from having to worry about thread management with synchronized blocks. For example, a Singleton can be implemented without concern for multiple threads inadvertently creating more than one initial instance. However, there are still cases in which one must watch out for race conditions, since ActionScript is full of event-driven callback mechanisms in which execution order cannot be known ahead of time (loading external assets and XML data, for example). In other words, while we don’t have to worry about two methods (or statements, for that matter) executing at the same time, or two threads executing the same method at the same time, we do still need to be concerned about the order in which methods execute. As far as I can tell, methods themselves can be guaranteed to execute atomically.

No Method Overloading

In Java, it’s possible to define more than one signature for a given method, varying based on the number, type, and ordering of parameters. In AS2.0, only one signature is allowed per method (including constructors). However, this disadvantage is offset somewhat by the fact that the AS compiler does not require that all parameters be specified when invoking a method; this allows us to write a method in which optional parameters follow required ones. For example, consider the constructor for the built-in Date object:

new Date(year, month [, date [, hour [, minute [, second [, millisecond ]]]]])

The Date constructor is written to accept seven parameters, but they are optional from least to most specific (right to left); that is, one could construct a Date by specifying only year, month, and date. In that case, the constructor would only see values for the first three parameters, and the rest would be undefined.

I’ll add more items to this list as I think of them.

The v2 component architecture introduced a new event dispatching and handling framework, in which event objects are produced by components and consumed by whatever listeners are attached to them. For more information on this, refer to the Flash documentation under “Using the event object”. The following sample code illustrates the basic principles.

listener = new Object();
listener.click = function(evtObj) {
trace("Button clicked: "+evt.target);
}
myButton.addEventListener("click", listener);

The myButton component generates a “click” event when pressed using the following code:

this.dispatchEvent({type:"click", target:this});

which creates a new (anonymous) event object (with the requisite type and target properties) and submits it to the event handling framework. The event framework then passes the event along to the listener object, which itself was previously registered as a listener for events of type “click” coming from myButton.

One very nice feature of the new event framework is that it is also possible to use it outside the context of v2 components. This can be useful, for example, when creating a modular application in which various elements need to communicate with one another yet making those elements into full-fledged components would be overkill.

Any object can be made into an event producer by invoking the EventDispatcher mix-in. A mix-in is a special class that dynamically adds methods and fields to a given object in order to expand its functionality. Such constructs are used liberally throughout the core classes in Flash MX 2004. Using the EventDispatcher mix-in adds the following methods to a given object:

dispatchEvent(evtObj):Void

Dispatches the given event object to the event framework, allowing it to be sent to any attached listeners

addEventListener(eventType:String, handler):Void

Registers the specified handler object as a listener for the specified event type coming from the current object

removeEventListener(eventType:String, handler):Void

Unregisters the specified handler for the specified event type

An additional method called dispatchQueue is also added, but that method is for internal use by the event framework.

In order to add these events to an object, simply invoke EventDispatcher.initialize:

class MyCalendar extends MovieClip {

public function MyCalendar(Void) {
...
// this adds (mixes in) the dispatchEvent, etc. methods
mx.events.EventDispatcher.initialize(this);
}
...
}

Once the initialize method is invoked at runtime, the MyCalendar class will have dispatchEvent and the add/remove listener methods, and will work just like any other event producer in the event framework.

One problem with the fact that the event methods are added through a mix-in at runtime is that the compiler won’t know about those methods while it is compiling the class. Therefore, if we just invoke dispatchEvent within the example class above, the compiler will generate an error since dispatchEvent isn’t defined within MyCalendar or within MovieClip, its superclass.

There are two ways to get around this problem. The easiest is just to define three additional class fields of type Function, one for each method that’s going to be added by the mixin:

class MyCalendar extends MovieClip {

var dispatchEvent:Function;
var addEventListener:Function;
var removeEventListener:Function;

public function MyCalendar(Void) {
...
// this adds (mixes in) the dispatchEvent, etc. methods
mx.events.EventDispatcher.initialize(this);
}
...
}

A slightly more rigorous method is to add a “stub” for each of the mix-in methods, which will be overridden once the mix-in attaches the real methods at runtime but will satisfy the compiler in the meantime. Write the stubs to throw Errors by default, just to make sure that the mix-in did in fact override those methods as expected:

class MyCalendar extends MovieClip {

public function MyCalendar(Void) {
...
// this adds (mixes in) the dispatchEvent, etc. methods
mx.events.EventDispatcher.initialize(this);
}

public function dispatchEvent(evtObj):Void {
throw new Error("dispatchEvent method not overridden!");
}

public function addEventListener(eventType:String, listener) {
throw new Error("addEventListener method not overridden!");
}

public function removeEventListener(eventType:String, listener) {
throw new Error("removeEventListener method not overridden!");
}

...
}

By doing this, the compiler will see that MyCalendar does in fact define the event methods, even though those methods will be overridden at runtime by the EventDispatcher class.

Two very useful additional properties of the event object passed to the hander for the Loader.progress event are evt.current (current number of bytes loaded) and evt.total (total bytes to load).

Example:

var loadListener = new Object();
loadListener.handleEvent = function(evt) {
if (evt.type == "progress") {
trace("PROGRESS: "+evt.current+" of "+evt.total);
}
};
myFormOrSlideOrWhatever.addEventListener("progress", loadListener);
myFormOrSlideOrWhatever.load();

Looks like the Loader component is probably based on the MovieClipLoader AS object. The MovieClipLoader.onLoadProgress entry in the docs mention the current and total properties of that event.

The docs say that this can be used to set a global style for a particular component:

_global.styles.RadioButton.setStyle("color", "blue");

What’s not quite clear from this is that before doing that, you’d need to create the style object for the RadioButton class first:

_global.styles.RadioButton = new mx.styles.CSSStyleDeclaration();

Otherwise you’re calling setStyle on a null reference.

Also interesting to note is the fact that setStyle is not actually a method of CSSStyleDeclaration. I don’t know where it comes from, actually (UIObject? Some kind of mixin?). Therefore you can’t use strict typing on style objects if you’re going to call setStyle on them.

Update: Got some more information on this from a response on FlashCoders. Turns out that setStyle does indeed come from a mixin — in short, UIComponent loads in UIComponentExtensions, which calls mx.styles.CSSSetStyle.enableRunTimeCSS(), which in turn causes the CSSSetStyle methods to be mixed in to CSSStyleDeclaration.