Table of Contents

AS2 FAQ

This is for all those questions about ActionScript2 specific issues and solutions that have become a reflex action, you hear ‘scope problem’ suddenly your leg jerks and you hear yourself saying ‘use Delegate’

Handling scope in event handlers

Flash is all about asyncronous events, so a major problem is how to get the right scope within the context of an event handler.

Take this code for example :

class Foo 
{
  function Foo() 
  {
    // Use a local variable to create the context within the event handler
    var self = this;
 
    sound = new Sound();
    sound.onLoad = function(success) { self.onSoundLoaded(success) };
    sound.onSoundComplete = function() { self.onSoundComplete() };
  }
}

This works, but to me it always smells a bit bad.

The most common solution is to use a Delegate. A Delegate is an object which will magically hide away your scope issues. When the event is triggered it will call your event handler, but the event handler will be run within a context which you can specify.

a simple example :

import mx.utils.Delegate
class Foo 
{
  private var loadedMessage:String;
  private var completeMessage:String;
  private var sound:Sound;
 
  function Foo() 
  {
    loadedMessage = "Hello, im in the right scope and the sound is ready";
    completeMessage = "Hello, im in the right scope and the sound is complete";
 
    sound = new Sound();
    sound.onLoad = Delegate.create(this,onSoundLoaded);
    sound.onSoundComplete = Delegate.create(this.onSoundComplete);
  }
 
  function onSoundLoaded(success:Boolean)
  {
    // Now 'this' refers to the object of type Foo
    // and we should get the right output
    trace(loadedMessage);
  }
 
  function onSoundComplete()
  {
    // Now 'this' refers to the object of type Foo
    // and we should get the right output
    trace(completeMessage);
  }
}

another common situation that can trip you up is when the method you need to call isnt in the scope of the defining class :

class Outside
{
  private var message:String = "The delegate found me.";
    
  public function myEventHandler()
  {
    trace(message);
  }
}

Which you want to call from this class :

import mx.utils.Delegate
class Foo 
{
  private var outside:Outside;
  private var sound:Sound;
 
  function Foo() 
  {
    outside = new Outside();
    sound = new Sound();
 
    // Remember to specify the full scope for the method
    sound.onLoad = Delegate.create(outside,outside.myEventHandler);
  }
}

The above examples use Macromedia’s Delegate class, which is fine for most situations, but sometimes you’ll want to handle other situations like passing parameters to the delegate when its created.

Heres just two other example implementations.

http://www.person13.com/articles/proxy/Proxy.htm

http://www.dynamicflash.co.uk/2005/02/delegate-class-refined/

Creating a class instance based on MovieClip without a symbol in the library

Note: This was discovered by Peter Joel, but I can’t find his original article. — Ian Thomas 2005/11/30 06:35

The trick hinges on the fact that each loaded class gets internally assigned a symbol name of _Packages.<class path> by Flash e.g. _Packages.net.somedomain.MyClass

Create your class:

class net.something.MyButton extends MovieClip
{
    public static var symbolName:String =          
          "__Packages.net.something.MyButton";
    private static var symbolLinked=
          Object.registerClass(symbolName, MyButton);
   
    public function MyButton()
    {
    }
 
    // Other implementation goes here...
}

To create an instance of it, use attachMovie and cast the returned object to your class:

import net.something.MyButton;
 
var button:MyButton=
   MyButton(myTimeline.attachMovie(
                     MyButton.symbolName, 
                    "someRandomButton",
                    myTimeline.getNextHighestDepth()));

Where myTimeline is whatever parent movie clip you’re trying to attach to. Now you can use button as an instance of MyButton without every having to go near the library...

An alternative way of writing it

Using the above theory as a base, you can make it all easier to use by simply creating a static create() class for your movieclip, like so:

class net.something.MyButton extends MovieClip
{
    private static var symbolName:String =          
          "__Packages.net.something.MyButton";
    private static var symbolLinked=
          Object.registerClass(symbolName, MyButton);
   
    public static function create(parent:MovieClip,
              initObject:Object,
              instanceName:String,
              depth:Number):MyButton
    {
       // A couple of defaults - and we don't care if
       // initObject is undefined...
       if (depth==undefined)
         depth=parent.getNextHighestDepth();
       if (instanceName==undefined)
         instanceName='clip'+depth;
 
       return MyButton(parent.attachMovie(symbolName,
               instanceName,depth,initObject));
    }
 
    public function MyButton()
    {
    }
 
    // Other implementation goes here...
}

Then all you need to do to use your MovieClip-derived class is:

import net.something.MyButton;
 
var button:MyButton=MyButton.create(myTimeline);

Why does my initializer get shared across all instances like it's static?

The symptom is usually something like the following:

// Rubbish class that'll do nothing useful.
class MyClass
{
  private var myList:Array=new Array();
 
  public function MyClass();
  {
  }
 
  public function pushItem(item:Object)
  {
    myList.push(item);
  }
 
  public function popItem():Object
  {
    return myList.pop();
  }
}
 
var a:MyClass=new MyClass();
var b:MyClass=new MyClass();
 
a.push("Hello");
b.push("Fred");
trace(a.pop()); // Outputs "Fred" - wtf?

It looks very much as if instances a and b are sharing the same myList variable - how come? It’s not as if I’ve put static in front of it...

This is down to the way that AS2 internally compiles to AS1, ‘faking’ a class structure using ECMAScript’s prototype feature.

Anything you use as an initialiser to a class property will be used as the initialiser for all instances of that class.

When it’s a primitive type (e.g. private var myInt:Number=10), that normally isn’t a problem, as you don’t manipulate primitives directly - you might replace them with other primitives (myInt=15) but you wouldn’t call methods on them directly.

When it’s anything other than a primitive - an Array, any class instance derived from Object - you’ll have problems. That initialising object will be shared between all instances.

To get around this, just don’t use initialisers. Do your initialisation in the constructor instead. The constructor gets called once for each new instance of the class; which is what we want to happen.

The following code works just fine:

// Rubbish class that'll do nothing useful.
class MyClass
{
  private var myList:Array;
 
  public function MyClass();
  {
    myList=new Array();
  }
 
  public function pushItem(item:Object)
  {
    myList.push(item);
  }
 
  public function popItem():Object
  {
    return myList.pop();
  }
}
 
var a:MyClass=new MyClass();
var b:MyClass=new MyClass();
 
a.push("Hello");
b.push("Fred");
trace(a.pop()); // Outputs "Hello" - as expected

Apparently in AS3 the behaviour is much more what you’d expect - no problems with shared initialisers.

Specifically, for those interested, class members that aren’t static are prototype members. During object construction, the constructor prototype is passed to the instance prototype; this transaction, by ECMAScript 262 standard, will pass primitive types by value, and complex types by refrence. As a result, primitive types always get the constructor prototype data, and complex types get the data the constructor prototype is pointing to, which is what all constructed objects of that class point to; meaning, if any object modifies complex prototype properties that were instantiated when the class prototype was constructed (any class property instantiated in the class body), all other objects, including the constructor prototype, returns the same data. Finally, this is because class properties instantiated in the class body are created when the class is defined; this is what gives complex types set in the class body the behaviour of static types, and this is why instantiating your types in the constructor instantiates a new instance of the type whenever an instance is constructed.