It’s very nice in AS3 to simply state the name of a function and get a Function variable back, regardless of whether or not it is a method, static method, dynamic function, or plain function in a package. Most other languages do not allow this level of convenience. Java doesn’t allow it at all and C++ and AS2 require fanciness in order to pass the this pointer. However, when you want a Function for the constructor of a class, you get a Class variable back. This article will show you a way to get a Function variable that will create an instance of a class when called.

Firstly, I feel as though I’m being teased by the constructor field of Object. Almost everything’s an Object, so it seems like it’d be really easy to get the constructor Function for anything. You’ll quickly find out that this is wrong though. Notice that the constructor field returns an Object, not a Function. But Function is a derivative of Object, so could it be that you’re actually just getting a Function back? Unfortunately no. Consider these tests:

trace("constructor of this: " + this["constructor"]);
trace("constructor of function: " + (function():void{})["constructor"]);
trace("constructor of class: " + Array["constructor"]);
var c:Class = Array["constructor"];
trace("constructor of class is class?: " + (c is Class));
var o:Object = new c();
trace("o: " + o);
trace("constructor of o: " + o["constructor"]);

This prints:

constructor of this: [class SimpleTestHarness]
constructor of function: [class Function]
constructor of class: [class Class]
constructor of class is class?: true

And then throws an error:

TypeError: Error #1115: Class$ is not a constructor.

So at no point do you ever get a Function object back which you could pass as a callback or otherwise use, which is our goal. You do get some Class objects back at various times, but they are not Function objects. The documentation goes on to say that “advanced” users may make a “constructor function”, but this is some very bizarre AS3 to write; it’s more like AS2 or JavaScript. We would like to simply get a Function for the constructor of any class we may have, just like we do for methods. We certainly don’t need to do anything “advanced” to get a Function for a method, we just state its name. This should go some way to explain why the following utility function is needed:

/**
*   Create a Function that, when called, instantiates a class
*   @author Jackson Dunstan
*   @param c Class to instantiate
*   @return A function that, when called, instantiates a class with the
*           arguments passed to said Function or null if the given class
*           is null.
*/
public static function makeConstructorFunction(c:Class): Function
{
	if (c == null)
	{
		return null;
	}
 
	/**
	*   The function to call to instantiate the class
	*   @param args Arguments to pass to the constructor. There may be up to
	*               20 arguments.
	*   @return The instantiated instance of the class or null if an instance
	*          couldn't be instantiated. This happens if the given class or
	*          arguments are null, there are more than 20 arguments, or the
	*          constructor of the class throws an exception.
	*/
	return function(...args:Array): Object
	{
		switch (args.length)
		{
			case 0:
				return new c();
				break;
			case 1:
				return new c(args[0]);
				break;
			case 2:
				return new c(args[0], args[1]);
				break;
			case 3:
				return new c(args[0], args[1], args[2]);
				break;
			case 4:
				return new c(args[0], args[1], args[2], args[3]);
				break;
			case 5:
				return new c(args[0], args[1], args[2], args[3], args[4]);
				break;
			case 6:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5]);
				break;
			case 7:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
				break;
			case 8:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
				break;
			case 9:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
				break;
			case 10:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
				break;
			case 11:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]);
				break;
			case 12:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11]);
				break;
			case 13:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12]);
				break;
			case 14:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13]);
				break;
			case 15:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14]);
				break;
			case 16:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15]);
				break;
			case 17:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16]);
				break;
			case 18:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17]);
				break;
			case 19:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17], args[18]);
				break;
			case 20:
				return new c(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17], args[18], args[19]);
				break;
			default:
				return null;
		}
		return null;
	}
}

This is indeed a hack! However, in the absence of any cleaner way, this seems to be all that is left to us. It yields the desired functionality with something very close to the desired simplicity:

var f:Function = makeConstructorFunction(Array);
trace("Constructor function for Array: " + f);
var a:Array = f(1, 2, 3, 4, 5);
trace("Created array: " + a + ". Has " + a.length + " elements.");

Which yields:

Constructor function for Array: function Function() {}
Created array: 1,2,3,4,5. Has 5 elements.

The usage is simple and straightforward. The speed is slower than directly calling the constructor due to the dynamic function call, dynamic usage of the class, the switch on the dynamic var args .length field, and the dynamic indexing into those same var args. So don’t use this if speed is a concern.

I loathe the above hack, but it’s necessary when you really need a Function for a constructor. At least I think it is. So I’ll end with a plea: if anyone knows a cleaner or faster way of doing this, please tell me (and everyone else) in the comments!