Quite often I have wanted to iterate over the public fields (and getters) of an arbitrary object. Sometimes this is for debugging purposes so I can print out the state of an object, particularly one that has all public fields like a C/C++ structure. Sadly, this is not possibly (with most objects) using the for-in and for-each loops we’ve come to know and love. So I made a utility function that I’m sharing with you all today. UPDATE: getProperties now supports MovieClip derivatives such as library symbols from an FLA

First things first: it’s possible that this utility function has already been created and posted somewhere, but some light internet searching has not turned anything up for me. That said, let’s look at how I implemented the function:

/**
*   Get the properties of an arbitrary object
*   @param obj Object to get the properties of
*   @return A list of the properties of the given object. This list is
*		   empty if the given object has no properties, is null, or is
*		   undefined.
*   @author Jackson Dunstan, JacksonDunstan.com
*/
public static function getProperties(obj:*): Vector.<String>
{
	var ret:Vector.<String> = new Vector.<String>();
 
	// Null and undefined have no properties
	if (obj == null)
	{
		return ret;
	}
 
	// Get properties from a for-in loop as long as the object isn't an
	// Array or Vector
	var description:XML = describeType(obj);
	if (!(obj is Array))
	{
		var name:String = String(description.attribute("name"));
		if (name.indexOf("__AS3__.vec::") < 0)
		{
			for (var k:String in obj)
			{
				ret.push(k);
			}
		}
	}
 
	// Add all properties from the describeType XML. This will be empty
	// for plain Objects, but they are covered by the above for-in loop
	var node:XML;
	for each (node in description.elements("accessor"))
	{
		ret.push(node.@name);
	}
	for each (node in description.elements("variable"))
	{
		ret.push(node.@name);
	}
 
	return ret;
}

The function is actually quite small, but there are a few implementation details to note:

  • null and undefined are handled by an == check with null because, unlike ===, the == operator will do type conversion to convert undefined to null.
  • The main way of determining the public variables and getters of an arbitrary object is the very handy (and very slow!) flash.utils.describeType function. It returns an XML object that describes the given object very verbosely, which is perfect for our uses.
  • In the case of dynamic Objects (e.g. o = {first:"Jackson", last:"Dunstan"}), describeType returns an empty XML. Its properties are obtained by the for-in loop.
  • All indices of Arrays and Vectors are skipped by exempting them from the for-in collection loop. While they are valid properties—in a sense—the returned Vector would be as long as the Array or Vector, which is quite undesirable for big lists.
  • The children of MovieClip derivatives, such as library symbols from FLAs, are not present in the describeType results, but are found by the for-in loop.

Next up I made a test app to make sure this function works with a variety of inputs: (getProperties omitted for brevity)

package
{
	import flash.text.*;
	import flash.utils.*;
	import flash.display.*;
 
	public class GetPropertiesTest extends Sprite
	{
		private var __logger:TextField = new TextField();
		private function log(msg:*): void { __logger.appendText(msg+"\n"); }
 
		public function GetPropertiesTest()
		{
			__logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(__logger);
 
			var testVals:Object = {
				sprite: new Sprite(),
				instance: new MyClass(),
				dynInstance: new MyDynamicClass(),
				array: [2,4,6,8],
				vector: Vector.<int>([2,4,6,8]),
				object: {first:"Jackson", last:"Dunstan"},
				dynFunction: function(arg1:int, arg2:int): void { },
				method: myMethod,
				number: 3.0,
				bool: true,
				classObj: MyClass
			};
			for (var testValProp:String in testVals)
			{
				var testVal:* = testVals[testValProp];
				var props:Vector.<String> = getProperties(testVal);
				log("Properties of " + testValProp + ":");
				for each (var prop:String in props)
				{
					log("\t" + prop);
				}
			}
		}
 
		private function myMethod(arg1:int, arg2:int): void { }
	}
}
import flash.display.*;
class MyParent
{
	public var publicVarParent:int;
	public static var publicStaticVarParent:int;
	private var privateVarParent:int;
	private static var privateStaticVarParent:int;
	private var protectedVarParent:int;
	private static var protectedStaticVarParent:int;
}
class MyClass extends MyParent
{
	public var publicVar:int;
	public static var publicStaticVar:int;
	private var privateVar:int;
	private static var privateStaticVar:int;
	private var protectedVar:int;
	private static var protectedStaticVar:int;
}
dynamic class MyDynamicClass extends MyParent
{
	public var publicVar:int;
	public static var publicStaticVar:int;
	private var privateVar:int;
	private static var privateStaticVar:int;
	private var protectedVar:int;
	private static var protectedStaticVar:int;
}

If you run this test yourself, you will probably have to use the context menu to “select all” and “copy” in order to get the whole output. Also, the order of the property names returned by getProperties is undefined, so you might not get results in the same order as I do. That said, here’s what I see with Flash Player plugin 10.1.102.64 on Mac OS X 10.6:

Properties of dynInstance:
	publicVar
	publicVarParent
Properties of array:
	length
Properties of classObj:
	prototype
	publicStaticVar
Properties of vector:
	fixed
	length
Properties of method:
	prototype
	length
Properties of bool:
Properties of sprite:
	scale9Grid
	soundTransform
	parent
	loaderInfo
	useHandCursor
	accessibilityProperties
	scaleX
	scaleY
	alpha
	blendShader
	buttonMode
	dropTarget
	numChildren
	textSnapshot
	width
	tabChildren
	height
	mouseChildren
	tabEnabled
	hitArea
	tabIndex
	focusRect
	root
	stage
	x
	y
	z
	visible
	name
	scaleZ
	mask
	mouseX
	graphics
	rotation
	mouseY
	rotationX
	rotationY
	rotationZ
	mouseEnabled
	cacheAsBitmap
	doubleClickEnabled
	opaqueBackground
	accessibilityImplementation
	scrollRect
	contextMenu
	filters
	blendMode
	transform
Properties of object:
	last
	first
Properties of number:
Properties of dynFunction:
	prototype
	length
Properties of instance:
	publicVar
	publicVarParent

I hope you find getProperties useful!